civetweb/resources/complete.lua
2018-03-31 23:17:00 +02:00

172 lines
4.7 KiB
Lua

#!/usr/bin/lua5.2
-- CivetWeb command line completion for bash
--[[ INSTALLING:
To use auto-completion for bash, you need to define a command for the bash built-in
[*complete*](https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion-Builtins.html).
Create a file called "civetweb" in the completion folder.
Depending on Linux distribution and version, this might be
/usr/share/bash-completion/completions/, /etc/bash_completion or another folder.
The file has to contain just one line:
complete -C /path/to/civetweb/resources/complete.lua civetweb
The complete command script is implemented in this file.
It needs Lua 5.2 to be installed (for Debian based systems: "sudo apt-get install lua5.2").
In case lua5.2 is not located in /usr/bin/lua5.2 (see "which lua5.2"),
the first line (#!) needs to be adapted accordingly.
--]]
---------------------------------------------------------------------------------------------------
-- The bash "complete -C" has an awkward interface:
-- see https://unix.stackexchange.com/questions/250262/how-to-use-bashs-complete-or-compgen-c-command-option
-- Command line arguments
cmd = arg[1] -- typically "./civetweb" or whatever path was used
this = arg[2] -- characters already typed for the next options
last = arg[3] -- option before this one
-- Environment variables
comp_line = os.getenv("COMP_LINE") -- entire command line
comp_point = os.getenv("COMP_POINT") -- position of cursor (index)
comp_type = os.getenv("COMP_TYPE") -- type:
-- 9 for normal completion
-- 33 when listing alternatives on ambiguous completions
-- 37 for menu completion
-- 63 when tabbing between ambiguous completions
-- 64 to list completions after a partial completion
-- Debug-Print function (must use absolute path for log file)
function dp(txt)
--[[ comment / uncomment to enable debugging
local f = io.open("/tmp/complete.log", "a");
f:write(txt .. "\n")
f:close()
--]]
end
-- Helper function: Check if files exist
function fileexists(name)
local f = io.open(name, "r")
if f then
f:close()
return true
end
return false
end
-- Debug logging
dp("complete: cmd=" .. cmd .. ", this=" .. this .. ", last=" .. last .. ", type=" .. comp_type)
-- Trim command line (remove spaces)
trim_comp_line = string.match(comp_line, "^%s*(.-)%s*$")
if (trim_comp_line == cmd) then
-- this is the first option
dp("Suggest --help argument")
print("--help ")
os.exit(0)
end
is_h = string.find(comp_line, "^%s*" .. cmd .. "%s+%-h%s")
is_h = is_h or string.find(comp_line, "^%s*" .. cmd .. "%s+%--help%s")
is_h = is_h or string.find(comp_line, "^%s*" .. cmd .. "%s+%-H%s")
if (is_h) then
dp("If --help is used, no additional argument is allowed")
os.exit(0)
end
is_a = string.find(comp_line, "^%s*" .. cmd .. "%s+%-A%s")
is_c = string.find(comp_line, "^%s*" .. cmd .. "%s+%-C%s")
is_i = string.find(comp_line, "^%s*" .. cmd .. "%s+%-I%s")
is_r = string.find(comp_line, "^%s*" .. cmd .. "%s+%-R%s")
if (is_i) then
dp("If --I is used, no additional argument is allowed")
os.exit(0)
end
-- -A and -R require the password file as second argument
htpasswd_r = ".htpasswd <mydomain.com> <username>"
htpasswd_a = htpasswd_r .. " <password>"
if (last == "-A") and (this == htpasswd_a:sub(1,#this)) then
dp("Fill with option template for -A")
print(htpasswd_a)
os.exit(0)
end
if (last == "-R") and (this == htpasswd_r:sub(1,#this)) then
dp("Fill with option template for -R")
print(htpasswd_r)
os.exit(0)
end
if (is_a or is_r) then
dp("Options -A and -R have a fixed number of arguments")
os.exit(0)
end
-- -C requires an URL, usually starting with http:// or https://
http = "http://"
if (last == "-C") and (this == http:sub(1,#this)) then
print(http)
print(http.. "localhost/")
os.exit(0)
end
http = "https://"
if (last == "-C") and (this == http:sub(1,#this)) then
print(http)
print(http.. "localhost/")
os.exit(0)
end
if (is_c) then
dp("Option -C has just one argument")
os.exit(0)
end
-- Take options directly from "--help" output of executable
optfile = "/tmp/civetweb.options"
if not fileexists(optfile) then
dp("options file " .. optfile .. " missing")
os.execute(cmd .. " --help > " .. optfile .. " 2>&1")
else
dp("options file " .. optfile .. " found")
end
for l in io.lines(optfile) do
local lkey, lval = l:match("^%s+(%-[^%s]*)%s*([^%s]*)%s*$")
if lkey then
local thislen = string.len(this)
if (thislen>0) and (this == lkey:sub(1,thislen)) then
print(lkey)
dp("match: " .. lkey)
keymatch = true
end
if last == lkey then
valmatch = lval
end
end
end
if keymatch then
-- at least one options matches
os.exit(0)
end
if valmatch then
-- suggest the default value
print(valmatch)
os.exit(0)
end
-- No context to determine next argument
dp("no specific option")
os.exit(0)