complete.lua 4.7 KB

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