Linux ip-172-26-2-223 5.4.0-1018-aws #18-Ubuntu SMP Wed Jun 24 01:15:00 UTC 2020 x86_64
Apache
: 172.26.2.223 | : 3.138.120.156
Cant Read [ /etc/named.conf ]
8.1.13
www
www.github.com/MadExploits
Terminal
AUTO ROOT
Adminer
Backdoor Destroyer
Linux Exploit
Lock Shell
Lock File
Create User
CREATE RDP
PHP Mailer
BACKCONNECT
UNLOCK SHELL
HASH IDENTIFIER
CPANEL RESET
CREATE WP USER
BLACK DEFEND!
README
+ Create Folder
+ Create File
/
usr /
share /
rspamd /
lualib /
[ HOME SHELL ]
Name
Size
Permission
Action
lua_content
[ DIR ]
drwxr-xr-x
lua_ffi
[ DIR ]
drwxr-xr-x
lua_magic
[ DIR ]
drwxr-xr-x
lua_scanners
[ DIR ]
drwxr-xr-x
lua_selectors
[ DIR ]
drwxr-xr-x
plugins
[ DIR ]
drwxr-xr-x
redis_scripts
[ DIR ]
drwxr-xr-x
rspamadm
[ DIR ]
drwxr-xr-x
ansicolors.lua
1.06
KB
-rw-r--r--
argparse.lua
56.6
KB
-rw-r--r--
fun.lua
28.77
KB
-rw-r--r--
global_functions.lua
1.79
KB
-rw-r--r--
lpegre.lua
6.3
KB
-rw-r--r--
lua_auth_results.lua
9.53
KB
-rw-r--r--
lua_aws.lua
8.99
KB
-rw-r--r--
lua_bayes_learn.lua
4.87
KB
-rw-r--r--
lua_bayes_redis.lua
8.16
KB
-rw-r--r--
lua_cfg_transform.lua
13.83
KB
-rw-r--r--
lua_cfg_utils.lua
2.59
KB
-rw-r--r--
lua_clickhouse.lua
16.22
KB
-rw-r--r--
lua_dkim_tools.lua
23.84
KB
-rw-r--r--
lua_fuzzy.lua
9.51
KB
-rw-r--r--
lua_lexer.lua
4.13
KB
-rw-r--r--
lua_maps.lua
17.5
KB
-rw-r--r--
lua_maps_expressions.lua
5.44
KB
-rw-r--r--
lua_meta.lua
11.96
KB
-rw-r--r--
lua_mime.lua
34.51
KB
-rw-r--r--
lua_mime_types.lua
27.18
KB
-rw-r--r--
lua_redis.lua
55.29
KB
-rw-r--r--
lua_settings.lua
8.81
KB
-rw-r--r--
lua_smtp.lua
5.05
KB
-rw-r--r--
lua_stat.lua
22.38
KB
-rw-r--r--
lua_tcp_sync.lua
4.39
KB
-rw-r--r--
lua_urls_compose.lua
7.46
KB
-rw-r--r--
lua_util.lua
47.21
KB
-rw-r--r--
lua_verdict.lua
5.18
KB
-rw-r--r--
lupa.lua
72.1
KB
-rw-r--r--
plugins_stats.lua
1.52
KB
-rw-r--r--
tableshape.lua
61.35
KB
-rw-r--r--
Delete
Unzip
Zip
${this.title}
Close
Code Editor : argparse.lua
-- The MIT License (MIT) -- Copyright (c) 2013 - 2018 Peter Melnichenko -- 2019 Paul Ouellette -- Permission is hereby granted, free of charge, to any person obtaining a copy of -- this software and associated documentation files (the "Software"), to deal in -- the Software without restriction, including without limitation the rights to -- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -- the Software, and to permit persons to whom the Software is furnished to do so, -- subject to the following conditions: -- The above copyright notice and this permission notice shall be included in all -- copies or substantial portions of the Software. -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. local function deep_update(t1, t2) for k, v in pairs(t2) do if type(v) == "table" then v = deep_update({}, v) end t1[k] = v end return t1 end -- A property is a tuple {name, callback}. -- properties.args is number of properties that can be set as arguments -- when calling an object. local function class(prototype, properties, parent) -- Class is the metatable of its instances. local cl = {} cl.__index = cl if parent then cl.__prototype = deep_update(deep_update({}, parent.__prototype), prototype) else cl.__prototype = prototype end if properties then local names = {} -- Create setter methods and fill set of property names. for _, property in ipairs(properties) do local name, callback = property[1], property[2] cl[name] = function(self, value) if not callback(self, value) then self["_" .. name] = value end return self end names[name] = true end function cl.__call(self, ...) -- When calling an object, if the first argument is a table, -- interpret keys as property names, else delegate arguments -- to corresponding setters in order. if type((...)) == "table" then for name, value in pairs((...)) do if names[name] then self[name](self, value) end end else local nargs = select("#", ...) for i, property in ipairs(properties) do if i > nargs or i > properties.args then break end local arg = select(i, ...) if arg ~= nil then self[property[1]](self, arg) end end end return self end end -- If indexing class fails, fallback to its parent. local class_metatable = {} class_metatable.__index = parent function class_metatable.__call(self, ...) -- Calling a class returns its instance. -- Arguments are delegated to the instance. local object = deep_update({}, self.__prototype) setmetatable(object, self) return object(...) end return setmetatable(cl, class_metatable) end local function typecheck(name, types, value) for _, type_ in ipairs(types) do if type(value) == type_ then return true end end error(("bad property '%s' (%s expected, got %s)"):format(name, table.concat(types, " or "), type(value))) end local function typechecked(name, ...) local types = {...} return {name, function(_, value) typecheck(name, types, value) end} end local multiname = {"name", function(self, value) typecheck("name", {"string"}, value) for alias in value:gmatch("%S+") do self._name = self._name or alias table.insert(self._aliases, alias) table.insert(self._public_aliases, alias) -- If alias contains '_', accept '-' also. if alias:find("_", 1, true) then table.insert(self._aliases, (alias:gsub("_", "-"))) end end -- Do not set _name as with other properties. return true end} local multiname_hidden = {"hidden_name", function(self, value) typecheck("hidden_name", {"string"}, value) for alias in value:gmatch("%S+") do table.insert(self._aliases, alias) if alias:find("_", 1, true) then table.insert(self._aliases, (alias:gsub("_", "-"))) end end return true end} local function parse_boundaries(str) if tonumber(str) then return tonumber(str), tonumber(str) end if str == "*" then return 0, math.huge end if str == "+" then return 1, math.huge end if str == "?" then return 0, 1 end if str:match "^%d+%-%d+$" then local min, max = str:match "^(%d+)%-(%d+)$" return tonumber(min), tonumber(max) end if str:match "^%d+%+$" then local min = str:match "^(%d+)%+$" return tonumber(min), math.huge end end local function boundaries(name) return {name, function(self, value) typecheck(name, {"number", "string"}, value) local min, max = parse_boundaries(value) if not min then error(("bad property '%s'"):format(name)) end self["_min" .. name], self["_max" .. name] = min, max end} end local actions = {} local option_action = {"action", function(_, value) typecheck("action", {"function", "string"}, value) if type(value) == "string" and not actions[value] then error(("unknown action '%s'"):format(value)) end end} local option_init = {"init", function(self) self._has_init = true end} local option_default = {"default", function(self, value) if type(value) ~= "string" then self._init = value self._has_init = true return true end end} local add_help = {"add_help", function(self, value) typecheck("add_help", {"boolean", "string", "table"}, value) if self._help_option_idx then table.remove(self._options, self._help_option_idx) self._help_option_idx = nil end if value then local help = self:flag() :description "Show this help message and exit." :action(function() print(self:get_help()) os.exit(0) end) if value ~= true then help = help(value) end if not help._name then help "-h" "--help" end self._help_option_idx = #self._options end end} local Parser = class({ _arguments = {}, _options = {}, _commands = {}, _mutexes = {}, _groups = {}, _require_command = true, _handle_options = true }, { args = 3, typechecked("name", "string"), typechecked("description", "string"), typechecked("epilog", "string"), typechecked("usage", "string"), typechecked("help", "string"), typechecked("require_command", "boolean"), typechecked("handle_options", "boolean"), typechecked("action", "function"), typechecked("command_target", "string"), typechecked("help_vertical_space", "number"), typechecked("usage_margin", "number"), typechecked("usage_max_width", "number"), typechecked("help_usage_margin", "number"), typechecked("help_description_margin", "number"), typechecked("help_max_width", "number"), add_help }) local Command = class({ _aliases = {}, _public_aliases = {} }, { args = 3, multiname, typechecked("description", "string"), typechecked("epilog", "string"), multiname_hidden, typechecked("summary", "string"), typechecked("target", "string"), typechecked("usage", "string"), typechecked("help", "string"), typechecked("require_command", "boolean"), typechecked("handle_options", "boolean"), typechecked("action", "function"), typechecked("command_target", "string"), typechecked("help_vertical_space", "number"), typechecked("usage_margin", "number"), typechecked("usage_max_width", "number"), typechecked("help_usage_margin", "number"), typechecked("help_description_margin", "number"), typechecked("help_max_width", "number"), typechecked("hidden", "boolean"), add_help }, Parser) local Argument = class({ _minargs = 1, _maxargs = 1, _mincount = 1, _maxcount = 1, _defmode = "unused", _show_default = true }, { args = 5, typechecked("name", "string"), typechecked("description", "string"), option_default, typechecked("convert", "function", "table"), boundaries("args"), typechecked("target", "string"), typechecked("defmode", "string"), typechecked("show_default", "boolean"), typechecked("argname", "string", "table"), typechecked("choices", "table"), typechecked("hidden", "boolean"), option_action, option_init }) local Option = class({ _aliases = {}, _public_aliases = {}, _mincount = 0, _overwrite = true }, { args = 6, multiname, typechecked("description", "string"), option_default, typechecked("convert", "function", "table"), boundaries("args"), boundaries("count"), multiname_hidden, typechecked("target", "string"), typechecked("defmode", "string"), typechecked("show_default", "boolean"), typechecked("overwrite", "boolean"), typechecked("argname", "string", "table"), typechecked("choices", "table"), typechecked("hidden", "boolean"), option_action, option_init }, Argument) function Parser:_inherit_property(name, default) local element = self while true do local value = element["_" .. name] if value ~= nil then return value end if not element._parent then return default end element = element._parent end end function Argument:_get_argument_list() local buf = {} local i = 1 while i <= math.min(self._minargs, 3) do local argname = self:_get_argname(i) if self._default and self._defmode:find "a" then argname = "[" .. argname .. "]" end table.insert(buf, argname) i = i+1 end while i <= math.min(self._maxargs, 3) do table.insert(buf, "[" .. self:_get_argname(i) .. "]") i = i+1 if self._maxargs == math.huge then break end end if i < self._maxargs then table.insert(buf, "...") end return buf end function Argument:_get_usage() local usage = table.concat(self:_get_argument_list(), " ") if self._default and self._defmode:find "u" then if self._maxargs > 1 or (self._minargs == 1 and not self._defmode:find "a") then usage = "[" .. usage .. "]" end end return usage end function actions.store_true(result, target) result[target] = true end function actions.store_false(result, target) result[target] = false end function actions.store(result, target, argument) result[target] = argument end function actions.count(result, target, _, overwrite) if not overwrite then result[target] = result[target] + 1 end end function actions.append(result, target, argument, overwrite) result[target] = result[target] or {} table.insert(result[target], argument) if overwrite then table.remove(result[target], 1) end end function actions.concat(result, target, arguments, overwrite) if overwrite then error("'concat' action can't handle too many invocations") end result[target] = result[target] or {} for _, argument in ipairs(arguments) do table.insert(result[target], argument) end end function Argument:_get_action() local action, init if self._maxcount == 1 then if self._maxargs == 0 then action, init = "store_true", nil else action, init = "store", nil end else if self._maxargs == 0 then action, init = "count", 0 else action, init = "append", {} end end if self._action then action = self._action end if self._has_init then init = self._init end if type(action) == "string" then action = actions[action] end return action, init end -- Returns placeholder for `narg`-th argument. function Argument:_get_argname(narg) local argname = self._argname or self:_get_default_argname() if type(argname) == "table" then return argname[narg] else return argname end end function Argument:_get_choices_list() return "{" .. table.concat(self._choices, ",") .. "}" end function Argument:_get_default_argname() if self._choices then return self:_get_choices_list() else return "<" .. self._name .. ">" end end function Option:_get_default_argname() if self._choices then return self:_get_choices_list() else return "<" .. self:_get_default_target() .. ">" end end -- Returns labels to be shown in the help message. function Argument:_get_label_lines() if self._choices then return {self:_get_choices_list()} else return {self._name} end end function Option:_get_label_lines() local argument_list = self:_get_argument_list() if #argument_list == 0 then -- Don't put aliases for simple flags like `-h` on different lines. return {table.concat(self._public_aliases, ", ")} end local longest_alias_length = -1 for _, alias in ipairs(self._public_aliases) do longest_alias_length = math.max(longest_alias_length, #alias) end local argument_list_repr = table.concat(argument_list, " ") local lines = {} for i, alias in ipairs(self._public_aliases) do local line = (" "):rep(longest_alias_length - #alias) .. alias .. " " .. argument_list_repr if i ~= #self._public_aliases then line = line .. "," end table.insert(lines, line) end return lines end function Command:_get_label_lines() return {table.concat(self._public_aliases, ", ")} end function Argument:_get_description() if self._default and self._show_default then if self._description then return ("%s (default: %s)"):format(self._description, self._default) else return ("default: %s"):format(self._default) end else return self._description or "" end end function Command:_get_description() return self._summary or self._description or "" end function Option:_get_usage() local usage = self:_get_argument_list() table.insert(usage, 1, self._name) usage = table.concat(usage, " ") if self._mincount == 0 or self._default then usage = "[" .. usage .. "]" end return usage end function Argument:_get_default_target() return self._name end function Option:_get_default_target() local res for _, alias in ipairs(self._public_aliases) do if alias:sub(1, 1) == alias:sub(2, 2) then res = alias:sub(3) break end end res = res or self._name:sub(2) return (res:gsub("-", "_")) end function Option:_is_vararg() return self._maxargs ~= self._minargs end function Parser:_get_fullname(exclude_root) local parent = self._parent if exclude_root and not parent then return "" end local buf = {self._name} while parent do if not exclude_root or parent._parent then table.insert(buf, 1, parent._name) end parent = parent._parent end return table.concat(buf, " ") end function Parser:_update_charset(charset) charset = charset or {} for _, command in ipairs(self._commands) do command:_update_charset(charset) end for _, option in ipairs(self._options) do for _, alias in ipairs(option._aliases) do charset[alias:sub(1, 1)] = true end end return charset end function Parser:argument(...) local argument = Argument(...) table.insert(self._arguments, argument) return argument end function Parser:option(...) local option = Option(...) table.insert(self._options, option) return option end function Parser:flag(...) return self:option():args(0)(...) end function Parser:command(...) local command = Command():add_help(true)(...) command._parent = self table.insert(self._commands, command) return command end function Parser:mutex(...) local elements = {...} for i, element in ipairs(elements) do local mt = getmetatable(element) assert(mt == Option or mt == Argument, ("bad argument #%d to 'mutex' (Option or Argument expected)"):format(i)) end table.insert(self._mutexes, elements) return self end function Parser:group(name, ...) assert(type(name) == "string", ("bad argument #1 to 'group' (string expected, got %s)"):format(type(name))) local group = {name = name, ...} for i, element in ipairs(group) do local mt = getmetatable(element) assert(mt == Option or mt == Argument or mt == Command, ("bad argument #%d to 'group' (Option or Argument or Command expected)"):format(i + 1)) end table.insert(self._groups, group) return self end local usage_welcome = "Usage: " function Parser:get_usage() if self._usage then return self._usage end local usage_margin = self:_inherit_property("usage_margin", #usage_welcome) local max_usage_width = self:_inherit_property("usage_max_width", 70) local lines = {usage_welcome .. self:_get_fullname()} local function add(s) if #lines[#lines]+1+#s <= max_usage_width then lines[#lines] = lines[#lines] .. " " .. s else lines[#lines+1] = (" "):rep(usage_margin) .. s end end -- Normally options are before positional arguments in usage messages. -- However, vararg options should be after, because they can't be reliable used -- before a positional argument. -- Mutexes come into play, too, and are shown as soon as possible. -- Overall, output usages in the following order: -- 1. Mutexes that don't have positional arguments or vararg options. -- 2. Options that are not in any mutexes and are not vararg. -- 3. Positional arguments - on their own or as a part of a mutex. -- 4. Remaining mutexes. -- 5. Remaining options. local elements_in_mutexes = {} local added_elements = {} local added_mutexes = {} local argument_to_mutexes = {} local function add_mutex(mutex, main_argument) if added_mutexes[mutex] then return end added_mutexes[mutex] = true local buf = {} for _, element in ipairs(mutex) do if not element._hidden and not added_elements[element] then if getmetatable(element) == Option or element == main_argument then table.insert(buf, element:_get_usage()) added_elements[element] = true end end end if #buf == 1 then add(buf[1]) elseif #buf > 1 then add("(" .. table.concat(buf, " | ") .. ")") end end local function add_element(element) if not element._hidden and not added_elements[element] then add(element:_get_usage()) added_elements[element] = true end end for _, mutex in ipairs(self._mutexes) do local is_vararg = false local has_argument = false for _, element in ipairs(mutex) do if getmetatable(element) == Option then if element:_is_vararg() then is_vararg = true end else has_argument = true argument_to_mutexes[element] = argument_to_mutexes[element] or {} table.insert(argument_to_mutexes[element], mutex) end elements_in_mutexes[element] = true end if not is_vararg and not has_argument then add_mutex(mutex) end end for _, option in ipairs(self._options) do if not elements_in_mutexes[option] and not option:_is_vararg() then add_element(option) end end -- Add usages for positional arguments, together with one mutex containing them, if they are in a mutex. for _, argument in ipairs(self._arguments) do -- Pick a mutex as a part of which to show this argument, take the first one that's still available. local mutex if elements_in_mutexes[argument] then for _, argument_mutex in ipairs(argument_to_mutexes[argument]) do if not added_mutexes[argument_mutex] then mutex = argument_mutex end end end if mutex then add_mutex(mutex, argument) else add_element(argument) end end for _, mutex in ipairs(self._mutexes) do add_mutex(mutex) end for _, option in ipairs(self._options) do add_element(option) end if #self._commands > 0 then if self._require_command then add("<command>") else add("[<command>]") end add("...") end return table.concat(lines, "\n") end local function split_lines(s) if s == "" then return {} end local lines = {} if s:sub(-1) ~= "\n" then s = s .. "\n" end for line in s:gmatch("([^\n]*)\n") do table.insert(lines, line) end return lines end local function autowrap_line(line, max_length) -- Algorithm for splitting lines is simple and greedy. local result_lines = {} -- Preserve original indentation of the line, put this at the beginning of each result line. -- If the first word looks like a list marker ('*', '+', or '-'), add spaces so that starts -- of the second and the following lines vertically align with the start of the second word. local indentation = line:match("^ *") if line:find("^ *[%*%+%-]") then indentation = indentation .. " " .. line:match("^ *[%*%+%-]( *)") end -- Parts of the last line being assembled. local line_parts = {} -- Length of the current line. local line_length = 0 -- Index of the next character to consider. local index = 1 while true do local word_start, word_finish, word = line:find("([^ ]+)", index) if not word_start then -- Ignore trailing spaces, if any. break end local preceding_spaces = line:sub(index, word_start - 1) index = word_finish + 1 if (#line_parts == 0) or (line_length + #preceding_spaces + #word <= max_length) then -- Either this is the very first word or it fits as an addition to the current line, add it. table.insert(line_parts, preceding_spaces) -- For the very first word this adds the indentation. table.insert(line_parts, word) line_length = line_length + #preceding_spaces + #word else -- Does not fit, finish current line and put the word into a new one. table.insert(result_lines, table.concat(line_parts)) line_parts = {indentation, word} line_length = #indentation + #word end end if #line_parts > 0 then table.insert(result_lines, table.concat(line_parts)) end if #result_lines == 0 then -- Preserve empty lines. result_lines[1] = "" end return result_lines end -- Automatically wraps lines within given array, -- attempting to limit line length to `max_length`. -- Existing line splits are preserved. local function autowrap(lines, max_length) local result_lines = {} for _, line in ipairs(lines) do local autowrapped_lines = autowrap_line(line, max_length) for _, autowrapped_line in ipairs(autowrapped_lines) do table.insert(result_lines, autowrapped_line) end end return result_lines end function Parser:_get_element_help(element) local label_lines = element:_get_label_lines() local description_lines = split_lines(element:_get_description()) local result_lines = {} -- All label lines should have the same length (except the last one, it has no comma). -- If too long, start description after all the label lines. -- Otherwise, combine label and description lines. local usage_margin_len = self:_inherit_property("help_usage_margin", 3) local usage_margin = (" "):rep(usage_margin_len) local description_margin_len = self:_inherit_property("help_description_margin", 25) local description_margin = (" "):rep(description_margin_len) local help_max_width = self:_inherit_property("help_max_width") if help_max_width then local description_max_width = math.max(help_max_width - description_margin_len, 10) description_lines = autowrap(description_lines, description_max_width) end if #label_lines[1] >= (description_margin_len - usage_margin_len) then for _, label_line in ipairs(label_lines) do table.insert(result_lines, usage_margin .. label_line) end for _, description_line in ipairs(description_lines) do table.insert(result_lines, description_margin .. description_line) end else for i = 1, math.max(#label_lines, #description_lines) do local label_line = label_lines[i] local description_line = description_lines[i] local line = "" if label_line then line = usage_margin .. label_line end if description_line and description_line ~= "" then line = line .. (" "):rep(description_margin_len - #line) .. description_line end table.insert(result_lines, line) end end return table.concat(result_lines, "\n") end local function get_group_types(group) local types = {} for _, element in ipairs(group) do types[getmetatable(element)] = true end return types end function Parser:_add_group_help(blocks, added_elements, label, elements) local buf = {label} for _, element in ipairs(elements) do if not element._hidden and not added_elements[element] then added_elements[element] = true table.insert(buf, self:_get_element_help(element)) end end if #buf > 1 then table.insert(blocks, table.concat(buf, ("\n"):rep(self:_inherit_property("help_vertical_space", 0) + 1))) end end function Parser:get_help() if self._help then return self._help end local blocks = {self:get_usage()} local help_max_width = self:_inherit_property("help_max_width") if self._description then local description = self._description if help_max_width then description = table.concat(autowrap(split_lines(description), help_max_width), "\n") end table.insert(blocks, description) end -- 1. Put groups containing arguments first, then other arguments. -- 2. Put remaining groups containing options, then other options. -- 3. Put remaining groups containing commands, then other commands. -- Assume that an element can't be in several groups. local groups_by_type = { [Argument] = {}, [Option] = {}, [Command] = {} } for _, group in ipairs(self._groups) do local group_types = get_group_types(group) for _, mt in ipairs({Argument, Option, Command}) do if group_types[mt] then table.insert(groups_by_type[mt], group) break end end end local default_groups = { {name = "Arguments", type = Argument, elements = self._arguments}, {name = "Options", type = Option, elements = self._options}, {name = "Commands", type = Command, elements = self._commands} } local added_elements = {} for _, default_group in ipairs(default_groups) do local type_groups = groups_by_type[default_group.type] for _, group in ipairs(type_groups) do self:_add_group_help(blocks, added_elements, group.name .. ":", group) end local default_label = default_group.name .. ":" if #type_groups > 0 then default_label = "Other " .. default_label:gsub("^.", string.lower) end self:_add_group_help(blocks, added_elements, default_label, default_group.elements) end if self._epilog then local epilog = self._epilog if help_max_width then epilog = table.concat(autowrap(split_lines(epilog), help_max_width), "\n") end table.insert(blocks, epilog) end return table.concat(blocks, "\n\n") end function Parser:add_help_command(value) if value then assert(type(value) == "string" or type(value) == "table", ("bad argument #1 to 'add_help_command' (string or table expected, got %s)"):format(type(value))) end local help = self:command() :description "Show help for commands." help:argument "command" :description "The command to show help for." :args "?" :action(function(_, _, cmd) if not cmd then print(self:get_help()) os.exit(0) else for _, command in ipairs(self._commands) do for _, alias in ipairs(command._aliases) do if alias == cmd then print(command:get_help()) os.exit(0) end end end end help:error(("unknown command '%s'"):format(cmd)) end) if value then help = help(value) end if not help._name then help "help" end help._is_help_command = true return self end function Parser:_is_shell_safe() if self._basename then if self._basename:find("[^%w_%-%+%.]") then return false end else for _, alias in ipairs(self._aliases) do if alias:find("[^%w_%-%+%.]") then return false end end end for _, option in ipairs(self._options) do for _, alias in ipairs(option._aliases) do if alias:find("[^%w_%-%+%.]") then return false end end if option._choices then for _, choice in ipairs(option._choices) do if choice:find("[%s'\"]") then return false end end end end for _, argument in ipairs(self._arguments) do if argument._choices then for _, choice in ipairs(argument._choices) do if choice:find("[%s'\"]") then return false end end end end for _, command in ipairs(self._commands) do if not command:_is_shell_safe() then return false end end return true end function Parser:add_complete(value) if value then assert(type(value) == "string" or type(value) == "table", ("bad argument #1 to 'add_complete' (string or table expected, got %s)"):format(type(value))) end local complete = self:option() :description "Output a shell completion script for the specified shell." :args(1) :choices {"bash", "zsh", "fish"} :action(function(_, _, shell) io.write(self["get_" .. shell .. "_complete"](self)) os.exit(0) end) if value then complete = complete(value) end if not complete._name then complete "--completion" end return self end function Parser:add_complete_command(value) if value then assert(type(value) == "string" or type(value) == "table", ("bad argument #1 to 'add_complete_command' (string or table expected, got %s)"):format(type(value))) end local complete = self:command() :description "Output a shell completion script." complete:argument "shell" :description "The shell to output a completion script for." :choices {"bash", "zsh", "fish"} :action(function(_, _, shell) io.write(self["get_" .. shell .. "_complete"](self)) os.exit(0) end) if value then complete = complete(value) end if not complete._name then complete "completion" end return self end local function base_name(pathname) return pathname:gsub("[/\\]*$", ""):match(".*[/\\]([^/\\]*)") or pathname end local function get_short_description(element) local short = element:_get_description():match("^(.-)%.%s") return short or element:_get_description():match("^(.-)%.?$") end function Parser:_get_options() local options = {} for _, option in ipairs(self._options) do for _, alias in ipairs(option._aliases) do table.insert(options, alias) end end return table.concat(options, " ") end function Parser:_get_commands() local commands = {} for _, command in ipairs(self._commands) do for _, alias in ipairs(command._aliases) do table.insert(commands, alias) end end return table.concat(commands, " ") end function Parser:_bash_option_args(buf, indent) local opts = {} for _, option in ipairs(self._options) do if option._choices or option._minargs > 0 then local compreply if option._choices then compreply = 'COMPREPLY=($(compgen -W "' .. table.concat(option._choices, " ") .. '" -- "$cur"))' else compreply = 'COMPREPLY=($(compgen -f -- "$cur"))' end table.insert(opts, (" "):rep(indent + 4) .. table.concat(option._aliases, "|") .. ")") table.insert(opts, (" "):rep(indent + 8) .. compreply) table.insert(opts, (" "):rep(indent + 8) .. "return 0") table.insert(opts, (" "):rep(indent + 8) .. ";;") end end if #opts > 0 then table.insert(buf, (" "):rep(indent) .. 'case "$prev" in') table.insert(buf, table.concat(opts, "\n")) table.insert(buf, (" "):rep(indent) .. "esac\n") end end function Parser:_bash_get_cmd(buf, indent) if #self._commands == 0 then return end table.insert(buf, (" "):rep(indent) .. 'args=("${args[@]:1}")') table.insert(buf, (" "):rep(indent) .. 'for arg in "${args[@]}"; do') table.insert(buf, (" "):rep(indent + 4) .. 'case "$arg" in') for _, command in ipairs(self._commands) do table.insert(buf, (" "):rep(indent + 8) .. table.concat(command._aliases, "|") .. ")") if self._parent then table.insert(buf, (" "):rep(indent + 12) .. 'cmd="$cmd ' .. command._name .. '"') else table.insert(buf, (" "):rep(indent + 12) .. 'cmd="' .. command._name .. '"') end table.insert(buf, (" "):rep(indent + 12) .. 'opts="$opts ' .. command:_get_options() .. '"') command:_bash_get_cmd(buf, indent + 12) table.insert(buf, (" "):rep(indent + 12) .. "break") table.insert(buf, (" "):rep(indent + 12) .. ";;") end table.insert(buf, (" "):rep(indent + 4) .. "esac") table.insert(buf, (" "):rep(indent) .. "done") end function Parser:_bash_cmd_completions(buf) local cmd_buf = {} if self._parent then self:_bash_option_args(cmd_buf, 12) end if #self._commands > 0 then table.insert(cmd_buf, (" "):rep(12) .. 'COMPREPLY=($(compgen -W "' .. self:_get_commands() .. '" -- "$cur"))') elseif self._is_help_command then table.insert(cmd_buf, (" "):rep(12) .. 'COMPREPLY=($(compgen -W "' .. self._parent:_get_commands() .. '" -- "$cur"))') end if #cmd_buf > 0 then table.insert(buf, (" "):rep(8) .. "'" .. self:_get_fullname(true) .. "')") table.insert(buf, table.concat(cmd_buf, "\n")) table.insert(buf, (" "):rep(12) .. ";;") end for _, command in ipairs(self._commands) do command:_bash_cmd_completions(buf) end end function Parser:get_bash_complete() self._basename = base_name(self._name) assert(self:_is_shell_safe()) local buf = {([[ _%s() { local IFS=$' \t\n' local args cur prev cmd opts arg args=("${COMP_WORDS[@]}") cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" opts="%s" ]]):format(self._basename, self:_get_options())} self:_bash_option_args(buf, 4) self:_bash_get_cmd(buf, 4) if #self._commands > 0 then table.insert(buf, "") table.insert(buf, (" "):rep(4) .. 'case "$cmd" in') self:_bash_cmd_completions(buf) table.insert(buf, (" "):rep(4) .. "esac\n") end table.insert(buf, ([=[ if [[ "$cur" = -* ]]; then COMPREPLY=($(compgen -W "$opts" -- "$cur")) fi } complete -F _%s -o bashdefault -o default %s ]=]):format(self._basename, self._basename)) return table.concat(buf, "\n") end function Parser:_zsh_arguments(buf, cmd_name, indent) if self._parent then table.insert(buf, (" "):rep(indent) .. "options=(") table.insert(buf, (" "):rep(indent + 2) .. "$options") else table.insert(buf, (" "):rep(indent) .. "local -a options=(") end for _, option in ipairs(self._options) do local line = {} if #option._aliases > 1 then if option._maxcount > 1 then table.insert(line, '"*"') end table.insert(line, "{" .. table.concat(option._aliases, ",") .. '}"') else table.insert(line, '"') if option._maxcount > 1 then table.insert(line, "*") end table.insert(line, option._name) end if option._description then local description = get_short_description(option):gsub('["%]:`$]', "\\%0") table.insert(line, "[" .. description .. "]") end if option._maxargs == math.huge then table.insert(line, ":*") end if option._choices then table.insert(line, ": :(" .. table.concat(option._choices, " ") .. ")") elseif option._maxargs > 0 then table.insert(line, ": :_files") end table.insert(line, '"') table.insert(buf, (" "):rep(indent + 2) .. table.concat(line)) end table.insert(buf, (" "):rep(indent) .. ")") table.insert(buf, (" "):rep(indent) .. "_arguments -s -S \\") table.insert(buf, (" "):rep(indent + 2) .. "$options \\") if self._is_help_command then table.insert(buf, (" "):rep(indent + 2) .. '": :(' .. self._parent:_get_commands() .. ')" \\') else for _, argument in ipairs(self._arguments) do local spec if argument._choices then spec = ": :(" .. table.concat(argument._choices, " ") .. ")" else spec = ": :_files" end if argument._maxargs == math.huge then table.insert(buf, (" "):rep(indent + 2) .. '"*' .. spec .. '" \\') break end for _ = 1, argument._maxargs do table.insert(buf, (" "):rep(indent + 2) .. '"' .. spec .. '" \\') end end if #self._commands > 0 then table.insert(buf, (" "):rep(indent + 2) .. '": :_' .. cmd_name .. '_cmds" \\') table.insert(buf, (" "):rep(indent + 2) .. '"*:: :->args" \\') end end table.insert(buf, (" "):rep(indent + 2) .. "&& return 0") end function Parser:_zsh_cmds(buf, cmd_name) table.insert(buf, "\n_" .. cmd_name .. "_cmds() {") table.insert(buf, " local -a commands=(") for _, command in ipairs(self._commands) do local line = {} if #command._aliases > 1 then table.insert(line, "{" .. table.concat(command._aliases, ",") .. '}"') else table.insert(line, '"' .. command._name) end if command._description then table.insert(line, ":" .. get_short_description(command):gsub('["`$]', "\\%0")) end table.insert(buf, " " .. table.concat(line) .. '"') end table.insert(buf, ' )\n _describe "command" commands\n}') end function Parser:_zsh_complete_help(buf, cmds_buf, cmd_name, indent) if #self._commands == 0 then return end self:_zsh_cmds(cmds_buf, cmd_name) table.insert(buf, "\n" .. (" "):rep(indent) .. "case $words[1] in") for _, command in ipairs(self._commands) do local name = cmd_name .. "_" .. command._name table.insert(buf, (" "):rep(indent + 2) .. table.concat(command._aliases, "|") .. ")") command:_zsh_arguments(buf, name, indent + 4) command:_zsh_complete_help(buf, cmds_buf, name, indent + 4) table.insert(buf, (" "):rep(indent + 4) .. ";;\n") end table.insert(buf, (" "):rep(indent) .. "esac") end function Parser:get_zsh_complete() self._basename = base_name(self._name) assert(self:_is_shell_safe()) local buf = {("#compdef %s\n"):format(self._basename)} local cmds_buf = {} table.insert(buf, "_" .. self._basename .. "() {") if #self._commands > 0 then table.insert(buf, " local context state state_descr line") table.insert(buf, " typeset -A opt_args\n") end self:_zsh_arguments(buf, self._basename, 2) self:_zsh_complete_help(buf, cmds_buf, self._basename, 2) table.insert(buf, "\n return 1") table.insert(buf, "}") local result = table.concat(buf, "\n") if #cmds_buf > 0 then result = result .. "\n" .. table.concat(cmds_buf, "\n") end return result .. "\n\n_" .. self._basename .. "\n" end local function fish_escape(string) return string:gsub("[\\']", "\\%0") end function Parser:_fish_get_cmd(buf, indent) if #self._commands == 0 then return end table.insert(buf, (" "):rep(indent) .. "set -e cmdline[1]") table.insert(buf, (" "):rep(indent) .. "for arg in $cmdline") table.insert(buf, (" "):rep(indent + 4) .. "switch $arg") for _, command in ipairs(self._commands) do table.insert(buf, (" "):rep(indent + 8) .. "case " .. table.concat(command._aliases, " ")) table.insert(buf, (" "):rep(indent + 12) .. "set cmd $cmd " .. command._name) command:_fish_get_cmd(buf, indent + 12) table.insert(buf, (" "):rep(indent + 12) .. "break") end table.insert(buf, (" "):rep(indent + 4) .. "end") table.insert(buf, (" "):rep(indent) .. "end") end function Parser:_fish_complete_help(buf, basename) local prefix = "complete -c " .. basename table.insert(buf, "") for _, command in ipairs(self._commands) do local aliases = table.concat(command._aliases, " ") local line if self._parent then line = ("%s -n '__fish_%s_using_command %s' -xa '%s'") :format(prefix, basename, self:_get_fullname(true), aliases) else line = ("%s -n '__fish_%s_using_command' -xa '%s'"):format(prefix, basename, aliases) end if command._description then line = ("%s -d '%s'"):format(line, fish_escape(get_short_description(command))) end table.insert(buf, line) end if self._is_help_command then local line = ("%s -n '__fish_%s_using_command %s' -xa '%s'") :format(prefix, basename, self:_get_fullname(true), self._parent:_get_commands()) table.insert(buf, line) end for _, option in ipairs(self._options) do local parts = {prefix} if self._parent then table.insert(parts, "-n '__fish_" .. basename .. "_seen_command " .. self:_get_fullname(true) .. "'") end for _, alias in ipairs(option._aliases) do if alias:match("^%-.$") then table.insert(parts, "-s " .. alias:sub(2)) elseif alias:match("^%-%-.+") then table.insert(parts, "-l " .. alias:sub(3)) end end if option._choices then table.insert(parts, "-xa '" .. table.concat(option._choices, " ") .. "'") elseif option._minargs > 0 then table.insert(parts, "-r") end if option._description then table.insert(parts, "-d '" .. fish_escape(get_short_description(option)) .. "'") end table.insert(buf, table.concat(parts, " ")) end for _, command in ipairs(self._commands) do command:_fish_complete_help(buf, basename) end end function Parser:get_fish_complete() self._basename = base_name(self._name) assert(self:_is_shell_safe()) local buf = {} if #self._commands > 0 then table.insert(buf, ([[ function __fish_%s_print_command set -l cmdline (commandline -poc) set -l cmd]]):format(self._basename)) self:_fish_get_cmd(buf, 4) table.insert(buf, ([[ echo "$cmd" end function __fish_%s_using_command test (__fish_%s_print_command) = "$argv" and return 0 or return 1 end function __fish_%s_seen_command string match -q "$argv*" (__fish_%s_print_command) and return 0 or return 1 end]]):format(self._basename, self._basename, self._basename, self._basename)) end self:_fish_complete_help(buf, self._basename) return table.concat(buf, "\n") .. "\n" end local function get_tip(context, wrong_name) local context_pool = {} local possible_name local possible_names = {} for name in pairs(context) do if type(name) == "string" then for i = 1, #name do possible_name = name:sub(1, i - 1) .. name:sub(i + 1) if not context_pool[possible_name] then context_pool[possible_name] = {} end table.insert(context_pool[possible_name], name) end end end for i = 1, #wrong_name + 1 do possible_name = wrong_name:sub(1, i - 1) .. wrong_name:sub(i + 1) if context[possible_name] then possible_names[possible_name] = true elseif context_pool[possible_name] then for _, name in ipairs(context_pool[possible_name]) do possible_names[name] = true end end end local first = next(possible_names) if first then if next(possible_names, first) then local possible_names_arr = {} for name in pairs(possible_names) do table.insert(possible_names_arr, "'" .. name .. "'") end table.sort(possible_names_arr) return "\nDid you mean one of these: " .. table.concat(possible_names_arr, " ") .. "?" else return "\nDid you mean '" .. first .. "'?" end else return "" end end local ElementState = class({ invocations = 0 }) function ElementState:__call(state, element) self.state = state self.result = state.result self.element = element self.target = element._target or element:_get_default_target() self.action, self.result[self.target] = element:_get_action() return self end function ElementState:error(fmt, ...) self.state:error(fmt, ...) end function ElementState:convert(argument, index) local converter = self.element._convert if converter then local ok, err if type(converter) == "function" then ok, err = converter(argument) elseif type(converter[index]) == "function" then ok, err = converter[index](argument) else ok = converter[argument] end if ok == nil then self:error(err and "%s" or "malformed argument '%s'", err or argument) end argument = ok end return argument end function ElementState:default(mode) return self.element._defmode:find(mode) and self.element._default end local function bound(noun, min, max, is_max) local res = "" if min ~= max then res = "at " .. (is_max and "most" or "least") .. " " end local number = is_max and max or min return res .. tostring(number) .. " " .. noun .. (number == 1 and "" or "s") end function ElementState:set_name(alias) self.name = ("%s '%s'"):format(alias and "option" or "argument", alias or self.element._name) end function ElementState:invoke() self.open = true self.overwrite = false if self.invocations >= self.element._maxcount then if self.element._overwrite then self.overwrite = true else local num_times_repr = bound("time", self.element._mincount, self.element._maxcount, true) self:error("%s must be used %s", self.name, num_times_repr) end else self.invocations = self.invocations + 1 end self.args = {} if self.element._maxargs <= 0 then self:close() end return self.open end function ElementState:check_choices(argument) if self.element._choices then for _, choice in ipairs(self.element._choices) do if argument == choice then return end end local choices_list = "'" .. table.concat(self.element._choices, "', '") .. "'" local is_option = getmetatable(self.element) == Option self:error("%s%s must be one of %s", is_option and "argument for " or "", self.name, choices_list) end end function ElementState:pass(argument) self:check_choices(argument) argument = self:convert(argument, #self.args + 1) table.insert(self.args, argument) if #self.args >= self.element._maxargs then self:close() end return self.open end function ElementState:complete_invocation() while #self.args < self.element._minargs do self:pass(self.element._default) end end function ElementState:close() if self.open then self.open = false if #self.args < self.element._minargs then if self:default("a") then self:complete_invocation() else if #self.args == 0 then if getmetatable(self.element) == Argument then self:error("missing %s", self.name) elseif self.element._maxargs == 1 then self:error("%s requires an argument", self.name) end end self:error("%s requires %s", self.name, bound("argument", self.element._minargs, self.element._maxargs)) end end local args if self.element._maxargs == 0 then args = self.args[1] elseif self.element._maxargs == 1 then if self.element._minargs == 0 and self.element._mincount ~= self.element._maxcount then args = self.args else args = self.args[1] end else args = self.args end self.action(self.result, self.target, args, self.overwrite) end end local ParseState = class({ result = {}, options = {}, arguments = {}, argument_i = 1, element_to_mutexes = {}, mutex_to_element_state = {}, command_actions = {} }) function ParseState:__call(parser, error_handler) self.parser = parser self.error_handler = error_handler self.charset = parser:_update_charset() self:switch(parser) return self end function ParseState:error(fmt, ...) self.error_handler(self.parser, fmt:format(...)) end function ParseState:switch(parser) self.parser = parser if parser._action then table.insert(self.command_actions, {action = parser._action, name = parser._name}) end for _, option in ipairs(parser._options) do option = ElementState(self, option) table.insert(self.options, option) for _, alias in ipairs(option.element._aliases) do self.options[alias] = option end end for _, mutex in ipairs(parser._mutexes) do for _, element in ipairs(mutex) do if not self.element_to_mutexes[element] then self.element_to_mutexes[element] = {} end table.insert(self.element_to_mutexes[element], mutex) end end for _, argument in ipairs(parser._arguments) do argument = ElementState(self, argument) table.insert(self.arguments, argument) argument:set_name() argument:invoke() end self.handle_options = parser._handle_options self.argument = self.arguments[self.argument_i] self.commands = parser._commands for _, command in ipairs(self.commands) do for _, alias in ipairs(command._aliases) do self.commands[alias] = command end end end function ParseState:get_option(name) local option = self.options[name] if not option then self:error("unknown option '%s'%s", name, get_tip(self.options, name)) else return option end end function ParseState:get_command(name) local command = self.commands[name] if not command then if #self.commands > 0 then self:error("unknown command '%s'%s", name, get_tip(self.commands, name)) else self:error("too many arguments") end else return command end end function ParseState:check_mutexes(element_state) if self.element_to_mutexes[element_state.element] then for _, mutex in ipairs(self.element_to_mutexes[element_state.element]) do local used_element_state = self.mutex_to_element_state[mutex] if used_element_state and used_element_state ~= element_state then self:error("%s can not be used together with %s", element_state.name, used_element_state.name) else self.mutex_to_element_state[mutex] = element_state end end end end function ParseState:invoke(option, name) self:close() option:set_name(name) self:check_mutexes(option, name) if option:invoke() then self.option = option end end function ParseState:pass(arg) if self.option then if not self.option:pass(arg) then self.option = nil end elseif self.argument then self:check_mutexes(self.argument) if not self.argument:pass(arg) then self.argument_i = self.argument_i + 1 self.argument = self.arguments[self.argument_i] end else local command = self:get_command(arg) self.result[command._target or command._name] = true if self.parser._command_target then self.result[self.parser._command_target] = command._name end self:switch(command) end end function ParseState:close() if self.option then self.option:close() self.option = nil end end function ParseState:finalize() self:close() for i = self.argument_i, #self.arguments do local argument = self.arguments[i] if #argument.args == 0 and argument:default("u") then argument:complete_invocation() else argument:close() end end if self.parser._require_command and #self.commands > 0 then self:error("a command is required") end for _, option in ipairs(self.options) do option.name = option.name or ("option '%s'"):format(option.element._name) if option.invocations == 0 then if option:default("u") then option:invoke() option:complete_invocation() option:close() end end local mincount = option.element._mincount if option.invocations < mincount then if option:default("a") then while option.invocations < mincount do option:invoke() option:close() end elseif option.invocations == 0 then self:error("missing %s", option.name) else self:error("%s must be used %s", option.name, bound("time", mincount, option.element._maxcount)) end end end for i = #self.command_actions, 1, -1 do self.command_actions[i].action(self.result, self.command_actions[i].name) end end function ParseState:parse(args) for _, arg in ipairs(args) do local plain = true if self.handle_options then local first = arg:sub(1, 1) if self.charset[first] then if #arg > 1 then plain = false if arg:sub(2, 2) == first then if #arg == 2 then if self.options[arg] then local option = self:get_option(arg) self:invoke(option, arg) else self:close() end self.handle_options = false else local equals = arg:find "=" if equals then local name = arg:sub(1, equals - 1) local option = self:get_option(name) if option.element._maxargs <= 0 then self:error("option '%s' does not take arguments", name) end self:invoke(option, name) self:pass(arg:sub(equals + 1)) else local option = self:get_option(arg) self:invoke(option, arg) end end else for i = 2, #arg do local name = first .. arg:sub(i, i) local option = self:get_option(name) self:invoke(option, name) if i ~= #arg and option.element._maxargs > 0 then self:pass(arg:sub(i + 1)) break end end end end end end if plain then self:pass(arg) end end self:finalize() return self.result end function Parser:error(msg) io.stderr:write(("%s\n\nError: %s\n"):format(self:get_usage(), msg)) os.exit(1) end -- Compatibility with strict.lua and other checkers: local default_cmdline = rawget(_G, "arg") or {} function Parser:_parse(args, error_handler) return ParseState(self, error_handler):parse(args or default_cmdline) end function Parser:parse(args) return self:_parse(args, self.error) end local function xpcall_error_handler(err) return tostring(err) .. "\noriginal " .. debug.traceback("", 2):sub(2) end function Parser:pparse(args) local parse_error local ok, result = xpcall(function() return self:_parse(args, function(_, err) parse_error = err error(err, 0) end) end, xpcall_error_handler) if ok then return true, result elseif not parse_error then error(result, 0) else return false, parse_error end end local argparse = {} argparse.version = "0.7.1" setmetatable(argparse, {__call = function(_, ...) return Parser(default_cmdline[0]):add_help(true)(...) end}) return argparse
Close