1#!/usr/bin/lua5.2 2 3-- CivetWeb command line completion for bash 4 5--[[ INSTALLING: 6 7To use auto-completion for bash, you need to define a command for the bash built-in 8[*complete*](https://www.gnu.org/software/bash/manual/html_node/Programmable-Completion-Builtins.html). 9 10Create a file called "civetweb" in the completion folder. 11Depending on Linux distribution and version, this might be 12/usr/share/bash-completion/completions/, /etc/bash_completion or another folder. 13 14The file has to contain just one line: 15complete -C /path/to/civetweb/resources/complete.lua civetweb 16 17The complete command script is implemented in this file. 18It needs Lua 5.2 to be installed (for Debian based systems: "sudo apt-get install lua5.2"). 19In case lua5.2 is not located in /usr/bin/lua5.2 (see "which lua5.2"), 20the first line (#!) needs to be adapted accordingly. 21 22--]] 23 24--------------------------------------------------------------------------------------------------- 25 26-- The bash "complete -C" has an awkward interface: 27-- see https://unix.stackexchange.com/questions/250262/how-to-use-bashs-complete-or-compgen-c-command-option 28-- Command line arguments 29cmd = arg[1] -- typically "./civetweb" or whatever path was used 30this = arg[2] -- characters already typed for the next options 31last = arg[3] -- option before this one 32-- Environment variables 33comp_line = os.getenv("COMP_LINE") -- entire command line 34comp_point = os.getenv("COMP_POINT") -- position of cursor (index) 35comp_type = os.getenv("COMP_TYPE") -- type: 36-- 9 for normal completion 37-- 33 when listing alternatives on ambiguous completions 38-- 37 for menu completion 39-- 63 when tabbing between ambiguous completions 40-- 64 to list completions after a partial completion 41 42 43-- Debug-Print function (must use absolute path for log file) 44function dp(txt) 45 --[[ comment / uncomment to enable debugging 46 local f = io.open("/tmp/complete.log", "a"); 47 f:write(txt .. "\n") 48 f:close() 49 --]] 50end 51 52-- Helper function: Check if files exist 53function fileexists(name) 54 local f = io.open(name, "r") 55 if f then 56 f:close() 57 return true 58 end 59 return false 60end 61 62 63-- Debug logging 64dp("complete: cmd=" .. cmd .. ", this=" .. this .. ", last=" .. last .. ", type=" .. comp_type) 65 66 67-- Trim command line (remove spaces) 68trim_comp_line = string.match(comp_line, "^%s*(.-)%s*$") 69 70if (trim_comp_line == cmd) then 71 -- this is the first option 72 dp("Suggest --help argument") 73 print("--help ") 74 os.exit(0) 75end 76 77is_h = string.find(comp_line, "^%s*" .. cmd .. "%s+%-h%s") 78is_h = is_h or string.find(comp_line, "^%s*" .. cmd .. "%s+%--help%s") 79is_h = is_h or string.find(comp_line, "^%s*" .. cmd .. "%s+%-H%s") 80 81if (is_h) then 82 dp("If --help is used, no additional argument is allowed") 83 os.exit(0) 84end 85 86is_a = string.find(comp_line, "^%s*" .. cmd .. "%s+%-A%s") 87is_c = string.find(comp_line, "^%s*" .. cmd .. "%s+%-C%s") 88is_i = string.find(comp_line, "^%s*" .. cmd .. "%s+%-I%s") 89is_r = string.find(comp_line, "^%s*" .. cmd .. "%s+%-R%s") 90 91if (is_i) then 92 dp("If --I is used, no additional argument is allowed") 93 os.exit(0) 94end 95 96-- -A and -R require the password file as second argument 97htpasswd_r = ".htpasswd <mydomain.com> <username>" 98htpasswd_a = htpasswd_r .. " <password>" 99if (last == "-A") and (this == htpasswd_a:sub(1,#this)) then 100 dp("Fill with option template for -A") 101 print(htpasswd_a) 102 os.exit(0) 103end 104if (last == "-R") and (this == htpasswd_r:sub(1,#this)) then 105 dp("Fill with option template for -R") 106 print(htpasswd_r) 107 os.exit(0) 108end 109if (is_a or is_r) then 110 dp("Options -A and -R have a fixed number of arguments") 111 os.exit(0) 112end 113 114-- -C requires an URL, usually starting with http:// or https:// 115http = "http://" 116if (last == "-C") and (this == http:sub(1,#this)) then 117 print(http) 118 print(http.. "localhost/") 119 os.exit(0) 120end 121http = "https://" 122if (last == "-C") and (this == http:sub(1,#this)) then 123 print(http) 124 print(http.. "localhost/") 125 os.exit(0) 126end 127if (is_c) then 128 dp("Option -C has just one argument") 129 os.exit(0) 130end 131 132 133-- Take options directly from "--help" output of executable 134optfile = "/tmp/civetweb.options" 135if not fileexists(optfile) then 136 dp("options file " .. optfile .. " missing") 137 os.execute(cmd .. " --help > " .. optfile .. " 2>&1") 138else 139 dp("options file " .. optfile .. " found") 140end 141 142for l in io.lines(optfile) do 143 local lkey, lval = l:match("^%s+(%-[^%s]*)%s*([^%s]*)%s*$") 144 if lkey then 145 local thislen = string.len(this) 146 if (thislen>0) and (this == lkey:sub(1,thislen)) then 147 print(lkey) 148 dp("match: " .. lkey) 149 keymatch = true 150 end 151 if last == lkey then 152 valmatch = lval 153 end 154 end 155end 156 157if keymatch then 158 -- at least one options matches 159 os.exit(0) 160end 161 162if valmatch then 163 -- suggest the default value 164 print(valmatch) 165 os.exit(0) 166end 167 168-- No context to determine next argument 169dp("no specific option") 170os.exit(0) 171 172