mirror of
https://codeberg.org/scip/jaildk.git
synced 2025-12-16 12:11:05 +01:00
added completion support
This commit is contained in:
5
Makefile
Normal file
5
Makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
all:
|
||||
bash bin/bash-completor -c completions.sh
|
||||
echo "JAILDIR=/jail" > jaildk-completion.bash
|
||||
cat _jaildk-completion.bash >> jaildk-completion.bash
|
||||
rm -f _jaildk-completion.bash
|
||||
734
bin/bash-completor
Executable file
734
bin/bash-completor
Executable file
@@ -0,0 +1,734 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -o errexit
|
||||
set -o nounset
|
||||
set -o pipefail
|
||||
set -o errtrace
|
||||
(shopt -p inherit_errexit &>/dev/null) && shopt -s inherit_errexit
|
||||
|
||||
readonly VERSION=v0.1.0
|
||||
readonly ARGS=$*
|
||||
readonly SPACES_8=' '
|
||||
readonly SPACES_6=' '
|
||||
readonly SPACES_4=' '
|
||||
readonly SPACES_2=' '
|
||||
|
||||
declare -r RED="\\e[31m"
|
||||
declare -r GREEN="\\e[32m"
|
||||
declare -r YELLOW="\\e[33m"
|
||||
declare -r CYAN="\\e[36m"
|
||||
declare -r RESET_ALL="\\e[0m"
|
||||
|
||||
debug() {
|
||||
printf "%b[Debug] %s%b\n" "$CYAN" "$*" "$RESET_ALL" >/dev/tty
|
||||
}
|
||||
|
||||
warn() {
|
||||
printf "%b[Warn] %s%b\n" "$YELLOW" "$*" "$RESET_ALL" >/dev/tty
|
||||
}
|
||||
|
||||
error() {
|
||||
printf "%b[Error] %s%b\n" "$RED" "$*" "$RESET_ALL" >/dev/tty
|
||||
}
|
||||
|
||||
suggest() {
|
||||
printf "%b[Suggest] %s%b\n" "$GREEN" "$*" "$RESET_ALL" >/dev/tty
|
||||
}
|
||||
|
||||
# Copy from https://github.com/adoyle-h/lobash/blob/develop/src/modules/is_array.bash
|
||||
is_array() {
|
||||
local attrs
|
||||
# shellcheck disable=2207
|
||||
attrs=$(declare -p "$1" 2>/dev/null | sed -E "s/^declare -([-a-zA-Z]+) .+/\\1/" || true)
|
||||
|
||||
# a: array
|
||||
# A: associate array
|
||||
if [[ ${attrs} =~ a|A ]]; then return 0; else return 1; fi
|
||||
}
|
||||
|
||||
is_func() {
|
||||
declare -F "$1" &>/dev/null
|
||||
}
|
||||
|
||||
get_varname() {
|
||||
local name=${1:-}
|
||||
local encoded=${word_to_varname[$name]:-}
|
||||
|
||||
if [[ -z ${encoded} ]]; then
|
||||
encoded=${name//[^a-zA-Z_]/_}
|
||||
fi
|
||||
|
||||
echo "${encoded}"
|
||||
}
|
||||
|
||||
is_gnu_sed() {
|
||||
local out
|
||||
out=$(${1:-sed} --version 2>/dev/null)
|
||||
[[ $out =~ 'GNU sed' ]]
|
||||
}
|
||||
|
||||
reply_words() {
|
||||
local IFS=$'\n'
|
||||
# shellcheck disable=2207
|
||||
COMPREPLY=( $(IFS=', ' compgen -W "$*" -- "${cur#=}") )
|
||||
}
|
||||
|
||||
reply_list() {
|
||||
local IFS=', '
|
||||
local array_list="" array_name
|
||||
# shellcheck disable=2068
|
||||
for array_name in $@; do
|
||||
array_list="$array_list \${${array_name}[*]}"
|
||||
done
|
||||
array_list="${array_list[*]:1}"
|
||||
|
||||
IFS=$'\n'' '
|
||||
eval "COMPREPLY=( \$(compgen -W \"$array_list\" -- \"\$cur\") )"
|
||||
}
|
||||
|
||||
reply_files() {
|
||||
local IFS=$'\n'
|
||||
compopt -o nospace -o filenames
|
||||
# shellcheck disable=2207
|
||||
COMPREPLY=( $(compgen -A file -- "$cur") )
|
||||
}
|
||||
|
||||
reply_files_in_pattern() {
|
||||
compopt -o nospace -o filenames
|
||||
|
||||
local path
|
||||
while read -r path; do
|
||||
if [[ $path =~ $1 ]] || [[ -d $path ]]; then
|
||||
COMPREPLY+=( "$path" )
|
||||
fi
|
||||
done < <(compgen -A file -- "$cur")
|
||||
}
|
||||
|
||||
reply_dirs() {
|
||||
local IFS=$'\n'
|
||||
compopt -o nospace -o filenames
|
||||
# shellcheck disable=2207
|
||||
COMPREPLY=( $(compgen -A directory -- "$cur") )
|
||||
}
|
||||
|
||||
|
||||
make_get_varnames() {
|
||||
echo ""
|
||||
|
||||
declare -p word_to_varname | sed -e "s/word_to_varname/_${cmd}_comp_word_to_varname/"
|
||||
|
||||
declare -f get_varname | sed -e "s/get_varname/_${cmd}_comp_util_get_varname/" -e 's/ *$//g' \
|
||||
-e "s/word_to_varname/_${cmd}_comp_word_to_varname/"
|
||||
}
|
||||
|
||||
make_dumped_variables() {
|
||||
echo ""
|
||||
local name
|
||||
for name in $(compgen -A variable var_); do
|
||||
declare -p "$name" | sed "s/^declare -.* var_/_${cmd}_comp_var_/"
|
||||
done
|
||||
}
|
||||
|
||||
make_header() {
|
||||
cat <<EOF
|
||||
# This file is generated by [bash-completor](https://github.com/adoyle-h/bash-completor/tree/$VERSION). Do not modify it manually.
|
||||
#
|
||||
# [Usage]
|
||||
# Put "source $output" in your bashrc.
|
||||
#
|
||||
# If you want to debug the completion.
|
||||
# Search '# Uncomment this line for debug' line in this file.
|
||||
#
|
||||
# [Update Script]
|
||||
# bash-completor $ARGS
|
||||
EOF
|
||||
|
||||
if [[ -n ${version:-} ]]; then
|
||||
cat <<EOF
|
||||
#
|
||||
# [Version] $version
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [[ -n ${license:-} ]]; then
|
||||
cat <<EOF
|
||||
#
|
||||
# [License] $license
|
||||
EOF
|
||||
fi
|
||||
|
||||
if (( ${#authors[@]} > 0 )); then
|
||||
cat <<EOF
|
||||
#
|
||||
# [Authors]
|
||||
EOF
|
||||
|
||||
{
|
||||
local IFS=$'\n'
|
||||
local author
|
||||
for author in "${authors[@]}"; do
|
||||
echo "# ${author}"
|
||||
done
|
||||
}
|
||||
fi
|
||||
|
||||
if (( ${#maintainers[@]} > 0 )); then
|
||||
cat <<EOF
|
||||
#
|
||||
# [Maintainers]
|
||||
EOF
|
||||
|
||||
{
|
||||
local IFS=$'\n'
|
||||
local maintainer
|
||||
for maintainer in "${maintainers[@]}"; do
|
||||
echo "# ${maintainer}"
|
||||
done
|
||||
}
|
||||
fi
|
||||
|
||||
if [[ -n ${description:-} ]]; then
|
||||
cat <<EOF
|
||||
#
|
||||
# [Description]
|
||||
# ${description}
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [[ -n ${notice:-} ]]; then
|
||||
cat <<EOF
|
||||
#
|
||||
# [Notice]
|
||||
# ${notice}
|
||||
EOF
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
|
||||
# shellcheck disable=2207
|
||||
# editorconfig-checker-disable
|
||||
EOF
|
||||
}
|
||||
|
||||
make_opts_variable() {
|
||||
local opts_varname=$1
|
||||
local -n opts=$opts_varname
|
||||
|
||||
printf '\n_%s_comp_%s=( ' "$cmd" "$opts_varname"
|
||||
for opt in "${opts[@]}"; do
|
||||
printf '%s ' "${opt/:*/}"
|
||||
done
|
||||
printf ')\n'
|
||||
}
|
||||
|
||||
parse_action() {
|
||||
local var=$1
|
||||
local position=$2
|
||||
|
||||
if [[ ${var:0:1} == '@' ]]; then
|
||||
case $var in
|
||||
@hold)
|
||||
printf ''
|
||||
;;
|
||||
|
||||
*)
|
||||
local func_name=${var:1}
|
||||
func_name=${func_name/:*/}
|
||||
|
||||
if [[ ${map_reply_funcs["reply_${func_name}"]:-} == true ]]; then
|
||||
local func_arg=${var/@${func_name}:/}
|
||||
if (( ${#func_arg} > 0 )) && [[ ${func_arg[*]:0:1} != '@' ]]; then
|
||||
printf -- "_%s_comp_reply_%s '%s'" "$cmd" "$func_name" "$func_arg"
|
||||
else
|
||||
printf -- '_%s_comp_reply_%s' "$cmd" "$func_name"
|
||||
fi
|
||||
else
|
||||
error "Invalid '$position': The action '$var' is not defined."
|
||||
|
||||
case $var in
|
||||
@f*) suggest "Try '@files' instead of '$var'." ;;
|
||||
@d*) suggest "Try '@dirs' instead of '$var'." ;;
|
||||
@h*) suggest "Try '@hold' instead of '$var'." ;;
|
||||
*) suggest "Try '@files', '@dirs', '@hold' or other reply functions. See https://github.com/adoyle-h/bash-completor/docs/syntax.md#reply-functions "
|
||||
esac
|
||||
|
||||
exit 5
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
else
|
||||
if [[ -n "$var" ]]; then
|
||||
printf -- "_%s_comp_reply_words '%s'" "$cmd" "$var"
|
||||
else
|
||||
printf ''
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
make_reply_action() {
|
||||
local varname=$1
|
||||
local -n var=$varname
|
||||
local reply
|
||||
|
||||
if [[ -v "$varname" ]]; then
|
||||
reply=$(parse_action "$var" "$varname=$var")
|
||||
elif is_array "$varname"; then
|
||||
reply="_${cmd}_comp_reply_list '${var}'"
|
||||
else
|
||||
reply="_${cmd}_comp_reply_files"
|
||||
fi
|
||||
|
||||
echo "$reply"
|
||||
}
|
||||
|
||||
make_reply_set() {
|
||||
cat <<EOF
|
||||
|
||||
_${cmd}_comp_reply_set() {
|
||||
local IFS=', '
|
||||
local array_list="" array_name
|
||||
# shellcheck disable=2068
|
||||
for array_name in \$@; do
|
||||
array_list="\$array_list \\\${_${cmd}_comp_var_\${array_name}[*]}"
|
||||
done
|
||||
array_list="\${array_list[*]:1}"
|
||||
|
||||
IFS=\$'\n'' '
|
||||
eval "COMPREPLY=( \\\$(compgen -W \"\$array_list\" -- \"\\\$cur\") )"
|
||||
}
|
||||
EOF
|
||||
|
||||
map_reply_funcs[reply_set]=true
|
||||
}
|
||||
|
||||
if is_gnu_sed; then
|
||||
# For GNU sed
|
||||
sed_reply_utils() {
|
||||
declare -f "$name" | sed -e "s/reply_/_${cmd}_comp_reply_/g" -e 's/ *$//g' |\
|
||||
sed -e ":a;N;\$!ba;s/IFS='\n'/IFS=\$'\\\\n'/g"
|
||||
}
|
||||
else
|
||||
# For BSD sed
|
||||
sed_reply_utils() {
|
||||
declare -f "$name" | sed -e "s/reply_/_${cmd}_comp_reply_/g" -e 's/ *$//g' |\
|
||||
sed -e ':a' -e 'N' -e '$!ba' -e "s/IFS='\n'/IFS=\$'\\\\n'/g"
|
||||
}
|
||||
fi
|
||||
|
||||
make_reply_utils() {
|
||||
local name
|
||||
|
||||
map_reply_funcs[reply_hold]=true
|
||||
|
||||
# Make framework and developer defined reply functions
|
||||
for name in $(compgen -A function reply_); do
|
||||
echo ""
|
||||
sed_reply_utils
|
||||
|
||||
map_reply_funcs[$name]=true
|
||||
done
|
||||
|
||||
make_reply_set
|
||||
|
||||
# Make developer custom reply functions
|
||||
for name in $(compgen -A variable reply_); do
|
||||
local -n list="$name"
|
||||
local func_name=${list[0]}
|
||||
|
||||
if is_func "$func_name"; then
|
||||
local rest=()
|
||||
local str
|
||||
for str in "${list[@]:1}"; do
|
||||
rest+=("'$str'")
|
||||
done
|
||||
|
||||
cat <<EOF
|
||||
|
||||
_${cmd}_comp_${name}() {
|
||||
_${cmd}_comp_${func_name} ${rest[@]}
|
||||
}
|
||||
EOF
|
||||
else
|
||||
error "Not found function '$func_name' for config '$name'"
|
||||
exit 7
|
||||
fi
|
||||
|
||||
map_reply_funcs[$name]=true
|
||||
done
|
||||
}
|
||||
|
||||
_make_cmd_option() {
|
||||
local opt=$1
|
||||
local indent=$2
|
||||
local default_action=$3
|
||||
|
||||
if [[ $opt =~ : ]]; then
|
||||
local option=${opt/:*/}
|
||||
local var=${opt/${option}:/}
|
||||
else
|
||||
local option=$opt
|
||||
local var=''
|
||||
fi
|
||||
|
||||
if [[ ${option: -1} == '=' ]]; then
|
||||
# skip --option=
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [[ $option == "$opt" ]]; then
|
||||
# skip --option without :
|
||||
return 0
|
||||
fi
|
||||
|
||||
local action
|
||||
action=$(parse_action "$var" "$opt")
|
||||
|
||||
# Skip to print case condition. Because this condition action is same to default_action.
|
||||
# default_action means "*) $default_action ;;"
|
||||
if [[ "$action" != "$default_action" ]]; then
|
||||
printf -- '%s) %s ;;\n' "$indent$option" "$action"
|
||||
fi
|
||||
}
|
||||
|
||||
_make_cmd_options() {
|
||||
echo " # rely the value of command option"
|
||||
local opt
|
||||
for opt in "${opts[@]}"; do
|
||||
_make_cmd_option "$opt" "$SPACES_6" "$reply_opts_fallback"
|
||||
done
|
||||
}
|
||||
|
||||
_make_equal_sign_option() {
|
||||
local opt=$1
|
||||
local indent="$SPACES_4"
|
||||
|
||||
if [[ $opt =~ : ]]; then
|
||||
local option=${opt/:*/}
|
||||
local var=${opt/${option}:/}
|
||||
else
|
||||
local option=$opt
|
||||
local var=''
|
||||
fi
|
||||
|
||||
if [[ $option =~ =$ ]]; then
|
||||
local action
|
||||
action=$(parse_action "$var" "$opt")
|
||||
printf -- '%s) %s ;;\n' "$indent$option" "$action"
|
||||
else
|
||||
if [[ $option =~ =[@-_a-zA-Z] ]]; then
|
||||
local recommend=${option/=/=:}
|
||||
recommend=${recommend// /,} # developer may use space delimiter
|
||||
warn "The option '$option' maybe missing the ':'. Do you need '${recommend}'?"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
_make_equal_sign_options() {
|
||||
for opt in "${opts[@]}"; do
|
||||
_make_equal_sign_option "$opt"
|
||||
done
|
||||
}
|
||||
|
||||
make_equal_sign_opts_func() {
|
||||
local opts_varname=$1
|
||||
local -n opts=$opts_varname
|
||||
|
||||
local equal_sign_options
|
||||
equal_sign_options=$(_make_equal_sign_options)
|
||||
|
||||
if [[ -n $equal_sign_options ]]; then
|
||||
cat <<EOF
|
||||
|
||||
_${cmd}_comp_equal_sign_${opts_varname}() {
|
||||
case "\${1}=" in
|
||||
$equal_sign_options
|
||||
esac
|
||||
}
|
||||
EOF
|
||||
map_equal_signs[${opts_varname}]=true
|
||||
fi
|
||||
}
|
||||
|
||||
make_cmd_core() {
|
||||
local opts_varname=$1
|
||||
local reply_args=$2
|
||||
local reply_opts_fallback=$3
|
||||
local -n opts=$opts_varname
|
||||
|
||||
if [[ " ${opts[*]} " == *' -- '* ]]; then
|
||||
cat <<EOF
|
||||
if [[ \$COMP_LINE == *' -- '* ]]; then
|
||||
# When current command line contains the "--" option, other options are forbidden.
|
||||
${reply_args}
|
||||
elif [[ \${cur:0:1} == [-+] ]]; then
|
||||
EOF
|
||||
else
|
||||
cat <<EOF
|
||||
if [[ \${cur:0:1} == [-+] ]]; then
|
||||
EOF
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
# rely options of command
|
||||
_${cmd}_comp_reply_list _${cmd}_comp_${opts_varname}
|
||||
EOF
|
||||
|
||||
if [[ "${opts[*]}" =~ = ]]; then
|
||||
# The options contain equal_sign
|
||||
cat <<EOF
|
||||
if [[ \${COMPREPLY[*]} =~ =\$ ]]; then compopt -o nospace; fi
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [[ -n ${map_equal_signs[${opts_varname}]:-} ]]; then
|
||||
cat <<EOF
|
||||
elif [[ \${cur} == = ]]; then
|
||||
_${cmd}_comp_equal_sign_${opts_varname} "\$prev"
|
||||
EOF
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
elif [[ \${prev:0:1} == [-+] ]]; then
|
||||
case "\${prev}" in
|
||||
$(_make_cmd_options)
|
||||
*) $reply_opts_fallback ;;
|
||||
esac
|
||||
EOF
|
||||
|
||||
if [[ -n ${map_equal_signs[${opts_varname}]:-} ]]; then
|
||||
cat <<EOF
|
||||
elif [[ \${prev} == = ]]; then
|
||||
_${cmd}_comp_equal_sign_${opts_varname} "\${COMP_WORDS[\$(( COMP_CWORD - 2 ))]}"
|
||||
EOF
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
else
|
||||
# rely the argument of command
|
||||
$reply_args
|
||||
fi
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
make_subcmd_opts() {
|
||||
local subcmd_opts
|
||||
for subcmd_opts in $(compgen -A variable subcmd_opts_); do
|
||||
make_opts_variable "$subcmd_opts"
|
||||
done
|
||||
}
|
||||
|
||||
make_subcmds() {
|
||||
cat <<EOF
|
||||
|
||||
_${cmd}_comp_subcmds=( ${subcmds[*]} )
|
||||
EOF
|
||||
}
|
||||
|
||||
make_subcmd_completion() {
|
||||
local subcmd_varname=$1
|
||||
|
||||
local reply_args
|
||||
if [[ -v "subcmd_args_${subcmd_varname}" ]]; then
|
||||
reply_args=$(make_reply_action "subcmd_args_${subcmd_varname}")
|
||||
else
|
||||
reply_args=$(make_reply_action subcmd_args__fallback)
|
||||
fi
|
||||
|
||||
local reply_opts_fallback
|
||||
if [[ -v "subcmd_opts_${subcmd_varname}_fallback" ]]; then
|
||||
reply_opts_fallback=$(make_reply_action "subcmd_opts_${subcmd_varname}_fallback")
|
||||
else
|
||||
reply_opts_fallback=$reply_args
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
|
||||
_${cmd}_completions_$subcmd_varname() {
|
||||
$(make_cmd_core "subcmd_opts_${subcmd_varname}" "$reply_args" "$reply_opts_fallback")
|
||||
}
|
||||
EOF
|
||||
}
|
||||
|
||||
make_subcmd_alias_completion() {
|
||||
local src
|
||||
for src in "${!subcmd_comp_alias[@]}"; do
|
||||
printf '_%s_completions_%s() { _%s_completions_%s; }\n' \
|
||||
"$cmd" "$(get_varname "$src")" "$cmd" "$(get_varname "${subcmd_comp_alias[$src]}")"
|
||||
done
|
||||
}
|
||||
|
||||
make_subcmd_completions() {
|
||||
local subcmd subcmd_varname
|
||||
for subcmd in "${subcmds[@]}"; do
|
||||
subcmd_varname=$(get_varname "$subcmd")
|
||||
if is_array "subcmd_opts_${subcmd_varname}"; then
|
||||
make_equal_sign_opts_func "subcmd_opts_${subcmd_varname}"
|
||||
make_subcmd_completion "$subcmd_varname"
|
||||
fi
|
||||
done
|
||||
|
||||
make_subcmd_alias_completion
|
||||
make_subcmd_completion _fallback
|
||||
}
|
||||
|
||||
|
||||
make_main_completion() {
|
||||
make_equal_sign_opts_func "cmd_opts"
|
||||
|
||||
cat <<EOF
|
||||
|
||||
_${cmd}_completions() {
|
||||
COMPREPLY=()
|
||||
local cur=\${COMP_WORDS[COMP_CWORD]}
|
||||
local prev=\${COMP_WORDS[COMP_CWORD-1]}
|
||||
|
||||
# Uncomment this line for debug
|
||||
# echo "[COMP_CWORD:\$COMP_CWORD][cur:\$cur][prev:\$prev][WORD_COUNT:\${#COMP_WORDS[*]}][COMP_WORDS:\${COMP_WORDS[*]}]" >> bash-debug.log
|
||||
EOF
|
||||
|
||||
local reply_args
|
||||
|
||||
if $has_subcmds; then
|
||||
cat <<EOF
|
||||
|
||||
if (( COMP_CWORD > 1 )); then
|
||||
# Enter the subcmd completion
|
||||
local subcmd_varname
|
||||
subcmd_varname="\$(_${cmd}_comp_util_get_varname "\${COMP_WORDS[1]}")"
|
||||
if type "_${cmd}_completions_\$subcmd_varname" &>/dev/null; then
|
||||
"_${cmd}_completions_\$subcmd_varname"
|
||||
else
|
||||
# If subcmd completion function not defined, use the fallback
|
||||
"_${cmd}_completions__fallback"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
EOF
|
||||
|
||||
reply_args="_${cmd}_comp_reply_list _${cmd}_comp_subcmds"
|
||||
else
|
||||
reply_args=$(make_reply_action cmd_args)
|
||||
fi
|
||||
|
||||
local reply_opts_fallback
|
||||
if [[ -v cmd_opts_fallback ]]; then
|
||||
reply_opts_fallback=$(make_reply_action cmd_opts_fallback)
|
||||
else
|
||||
reply_opts_fallback=$(make_reply_action cmd_args)
|
||||
fi
|
||||
|
||||
cat <<EOF
|
||||
|
||||
# Enter the cmd completion
|
||||
$(make_cmd_core "cmd_opts" "$reply_args" "$reply_opts_fallback")
|
||||
}
|
||||
|
||||
complete -F _${cmd}_completions -o bashdefault ${cmd_name}
|
||||
# vi: sw=2 ts=2
|
||||
EOF
|
||||
}
|
||||
|
||||
|
||||
make() {
|
||||
make_header
|
||||
make_opts_variable cmd_opts
|
||||
make_dumped_variables
|
||||
|
||||
if $has_subcmds; then
|
||||
make_subcmd_opts
|
||||
make_get_varnames
|
||||
fi
|
||||
|
||||
make_reply_utils
|
||||
|
||||
if $has_subcmds; then
|
||||
make_subcmds
|
||||
make_subcmd_completions
|
||||
fi
|
||||
make_main_completion
|
||||
}
|
||||
|
||||
do_make() {
|
||||
# NOTE: Naming variable should avoid some prefixes like "reply_" and "subcmd_opts_". Search "compgen -A".
|
||||
local conf_path=$1
|
||||
local has_subcmds=false
|
||||
local equal_sign_idx=0
|
||||
local -A map_reply_funcs=() map_equal_signs=()
|
||||
local output cmd cmd_name cmd_args notice
|
||||
local -a authors=() maintainers=() subcmds=() cmd_opts=()
|
||||
local -A subcmd_comp_alias=() word_to_varname=()
|
||||
|
||||
check_conf "$conf_path"
|
||||
|
||||
local output_path
|
||||
output_path="$(dirname "$conf_path")/$output"
|
||||
make > "$output_path"
|
||||
printf '%bGenerated file: %s%b\n' "${GREEN}" "$output_path" "$RESET_ALL"
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: bash-completor [options]
|
||||
|
||||
Options:
|
||||
-c <config_path> To generate Bash completion script based on configuration
|
||||
-h|--help Print the usage
|
||||
--version Print the version of bash-completor
|
||||
|
||||
Description: Quickly generate Bash completion script based on configuration.
|
||||
|
||||
Config Syntax: https://github.com/adoyle-h/bash-completor/docs/syntax.md
|
||||
|
||||
Project: https://github.com/adoyle-h/bash-completor
|
||||
|
||||
Version: $VERSION
|
||||
EOF
|
||||
}
|
||||
|
||||
check_conf() {
|
||||
local conf_path=$1
|
||||
|
||||
if [[ ! -f $conf_path ]]; then
|
||||
echo "Not found config file at $conf_path" >&2
|
||||
exit 3
|
||||
fi
|
||||
|
||||
# shellcheck disable=1090
|
||||
. "$conf_path"
|
||||
|
||||
# Set default values of config options
|
||||
cmd_name=$cmd
|
||||
cmd=$(get_varname "$cmd_name")
|
||||
cmd_args=${cmd_args:-@files}
|
||||
subcmd_args__fallback=${subcmd_args__fallback:-@files}
|
||||
|
||||
if (( ${#subcmds[@]} > 0 )); then
|
||||
has_subcmds=true
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
if (( $# == 0 )); then usage; exit 0; fi
|
||||
|
||||
case "$1" in
|
||||
-c)
|
||||
do_make "$2"
|
||||
;;
|
||||
|
||||
-h|--help)
|
||||
usage
|
||||
;;
|
||||
|
||||
--version)
|
||||
echo "$VERSION"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Invalid option '$1'." >&2
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
99
completions.sh
Normal file
99
completions.sh
Normal file
@@ -0,0 +1,99 @@
|
||||
output=_jaildk-completion.bash
|
||||
cmd=jaildk
|
||||
cmd_opts=()
|
||||
|
||||
subcmds=(base build create clone fetchports install uninstall remove
|
||||
reinstall prune start stop restart status rc ipfw login
|
||||
blogin freeze thaw help version update)
|
||||
|
||||
reply_jail() {
|
||||
local jails=$(ls $JAILDIR/etc)
|
||||
COMPREPLY=( $(compgen -W "${jails[*]}" -- "$cur") )
|
||||
}
|
||||
|
||||
reply_base() {
|
||||
local bases=$(ls $JAILDIR/base)
|
||||
COMPREPLY=( $(compgen -W "${bases[*]}" -- "$cur") )
|
||||
}
|
||||
|
||||
reply_version() {
|
||||
local versions=$(ls -d $JAILDIR/etc/*/etc-*|cut -d- -f2 | sort -u)
|
||||
COMPREPLY=( $(compgen -W "${versions[*]}" -- "$cur") )
|
||||
}
|
||||
|
||||
rcscripts='mount,ports,mtree,pf'
|
||||
modes='start,stop,status,restart'
|
||||
|
||||
### sub cmd base
|
||||
subcmd_opts_base=(-b -w)
|
||||
|
||||
### sub cmd build
|
||||
# FIXME: -m
|
||||
subcmd_opts_build=(-b:@base -v:@version -m:$modes)
|
||||
subcmd_args_build=@jail
|
||||
|
||||
### sub cmd clone
|
||||
# FIXME: how to fetch version from already selected jail
|
||||
subcmd_opts_clone=(-s:@jail -d:@jail -o:@version -n:@version)
|
||||
|
||||
### sub cmd fetchports
|
||||
subcmd_opts_fetchports=(-v:@version)
|
||||
|
||||
### sub cmd install
|
||||
# FIXME: -m
|
||||
subcmd_opts_install=(-m:$modes -r:$rcscripts)
|
||||
subcmd_args_install=@jail
|
||||
|
||||
### sub cmd uninstall
|
||||
subcmd_opts_uninstall=(-w)
|
||||
subcmd_args_uninstall=@jail
|
||||
|
||||
### sub cmd remove
|
||||
subcmd_args_remove=@jail
|
||||
|
||||
### sub cmd reinstall
|
||||
subcmd_opts_reinstall=(-b:@base -v:@version)
|
||||
subcmd_args_reinstall=@jail
|
||||
|
||||
### sub cmd prune
|
||||
subcmd_opts_prune=(-b -a -j:@jail)
|
||||
|
||||
### sub cmd start
|
||||
subcmd_args_start=@jail
|
||||
|
||||
### sub cmd stop
|
||||
subcmd_args_stop=@jail
|
||||
|
||||
### sub cmd restart
|
||||
subcmd_args_restart=@jail
|
||||
|
||||
### sub cmd status
|
||||
subcmd_opts_status=(-v)
|
||||
subcmd_args_status=@jail
|
||||
|
||||
### sub cmd rc
|
||||
subcmd_opts_rc=(-m:$modes -r:$rcscripts)
|
||||
|
||||
### sub cmd ipfw
|
||||
subcmd_opts_ipfw=(-m:$modes)
|
||||
subcmd_args_ipfw=@jail
|
||||
|
||||
### sub cmd login
|
||||
subcmd_args_login=@jail
|
||||
|
||||
### sub cmd blogin
|
||||
subcmd_args_blogin=@jail
|
||||
|
||||
### sub cmd freeze
|
||||
subcmd_opts_freeze=(-a -b -v:@version)
|
||||
subcmd_args_freeze=@jail
|
||||
|
||||
### sub cmd thaw
|
||||
subcmd_args_thaw=@files
|
||||
|
||||
### sub cmd help
|
||||
subcmd_args_help="${subcmds[*]}"
|
||||
|
||||
### sub cmd update
|
||||
subcmd_opts_update=(-f)
|
||||
|
||||
397
jaildk-completion.bash
Normal file
397
jaildk-completion.bash
Normal file
@@ -0,0 +1,397 @@
|
||||
JAILDIR=/jail
|
||||
# This file is generated by [bash-completor](https://github.com/adoyle-h/bash-completor/tree/v0.1.0). Do not modify it manually.
|
||||
#
|
||||
# [Usage]
|
||||
# Put "source _jaildk-completion.bash" in your bashrc.
|
||||
#
|
||||
# If you want to debug the completion.
|
||||
# Search '# Uncomment this line for debug' line in this file.
|
||||
#
|
||||
# [Update Script]
|
||||
# bash-completor -c completions.sh
|
||||
|
||||
# shellcheck disable=2207
|
||||
# editorconfig-checker-disable
|
||||
|
||||
_jaildk_comp_cmd_opts=( )
|
||||
|
||||
|
||||
_jaildk_comp_subcmd_opts_base=( -b -w )
|
||||
|
||||
_jaildk_comp_subcmd_opts_build=( -b -v -m )
|
||||
|
||||
_jaildk_comp_subcmd_opts_clone=( -s -d -o -n )
|
||||
|
||||
_jaildk_comp_subcmd_opts_fetchports=( -v )
|
||||
|
||||
_jaildk_comp_subcmd_opts_freeze=( -a -b -v )
|
||||
|
||||
_jaildk_comp_subcmd_opts_install=( -m -r )
|
||||
|
||||
_jaildk_comp_subcmd_opts_ipfw=( -m )
|
||||
|
||||
_jaildk_comp_subcmd_opts_prune=( -b -a -j )
|
||||
|
||||
_jaildk_comp_subcmd_opts_rc=( -m -r )
|
||||
|
||||
_jaildk_comp_subcmd_opts_reinstall=( -b -v )
|
||||
|
||||
_jaildk_comp_subcmd_opts_status=( -v )
|
||||
|
||||
_jaildk_comp_subcmd_opts_uninstall=( -w )
|
||||
|
||||
_jaildk_comp_subcmd_opts_update=( -f )
|
||||
|
||||
declare -A _jaildk_comp_word_to_varname=()
|
||||
_jaildk_comp_util_get_varname ()
|
||||
{
|
||||
local name=${1:-};
|
||||
local encoded=${_jaildk_comp_word_to_varname[$name]:-};
|
||||
if [[ -z ${encoded} ]]; then
|
||||
encoded=${name//[^a-zA-Z_]/_};
|
||||
fi;
|
||||
echo "${encoded}"
|
||||
}
|
||||
|
||||
_jaildk_comp_reply_base ()
|
||||
{
|
||||
local bases=$(ls $JAILDIR/base);
|
||||
COMPREPLY=($(compgen -W "${bases[*]}" -- "$cur"))
|
||||
}
|
||||
|
||||
_jaildk_comp_reply_dirs ()
|
||||
{
|
||||
local IFS=$'\n';
|
||||
compopt -o nospace -o filenames;
|
||||
COMPREPLY=($(compgen -A directory -- "$cur"))
|
||||
}
|
||||
|
||||
_jaildk_comp_reply_files ()
|
||||
{
|
||||
local IFS=$'\n';
|
||||
compopt -o nospace -o filenames;
|
||||
COMPREPLY=($(compgen -A file -- "$cur"))
|
||||
}
|
||||
|
||||
_jaildk_comp_reply_files_in_pattern ()
|
||||
{
|
||||
compopt -o nospace -o filenames;
|
||||
local path;
|
||||
while read -r path; do
|
||||
if [[ $path =~ $1 ]] || [[ -d $path ]]; then
|
||||
COMPREPLY+=("$path");
|
||||
fi;
|
||||
done < <(compgen -A file -- "$cur")
|
||||
}
|
||||
|
||||
_jaildk_comp_reply_jail ()
|
||||
{
|
||||
local jails=$(ls $JAILDIR/etc);
|
||||
COMPREPLY=($(compgen -W "${jails[*]}" -- "$cur"))
|
||||
}
|
||||
|
||||
_jaildk_comp_reply_list ()
|
||||
{
|
||||
local IFS=', ';
|
||||
local array_list="" array_name;
|
||||
for array_name in $@;
|
||||
do
|
||||
array_list="$array_list \${${array_name}[*]}";
|
||||
done;
|
||||
array_list="${array_list[*]:1}";
|
||||
IFS=$'\n'' ';
|
||||
eval "COMPREPLY=( \$(compgen -W \"$array_list\" -- \"\$cur\") )"
|
||||
}
|
||||
|
||||
_jaildk_comp_reply_version ()
|
||||
{
|
||||
local versions=$(ls -d $JAILDIR/etc/*/etc-*|cut -d- -f2 | sort -u);
|
||||
COMPREPLY=($(compgen -W "${versions[*]}" -- "$cur"))
|
||||
}
|
||||
|
||||
_jaildk_comp_reply_words ()
|
||||
{
|
||||
local IFS=$'\n';
|
||||
COMPREPLY=($(IFS=', ' compgen -W "$*" -- "${cur#=}"))
|
||||
}
|
||||
|
||||
_jaildk_comp_reply_set() {
|
||||
local IFS=', '
|
||||
local array_list="" array_name
|
||||
# shellcheck disable=2068
|
||||
for array_name in $@; do
|
||||
array_list="$array_list \${_jaildk_comp_var_${array_name}[*]}"
|
||||
done
|
||||
array_list="${array_list[*]:1}"
|
||||
|
||||
IFS=$'\n'' '
|
||||
eval "COMPREPLY=( \$(compgen -W \"$array_list\" -- \"\$cur\") )"
|
||||
}
|
||||
|
||||
_jaildk_comp_subcmds=( base build create clone fetchports install uninstall remove reinstall prune start stop restart status rc ipfw login blogin freeze thaw help version update )
|
||||
|
||||
_jaildk_completions_base() {
|
||||
if [[ ${cur:0:1} == [-+] ]]; then
|
||||
# rely options of command
|
||||
_jaildk_comp_reply_list _jaildk_comp_subcmd_opts_base
|
||||
elif [[ ${prev:0:1} == [-+] ]]; then
|
||||
case "${prev}" in
|
||||
# rely the value of command option
|
||||
*) _jaildk_comp_reply_files ;;
|
||||
esac
|
||||
else
|
||||
# rely the argument of command
|
||||
_jaildk_comp_reply_files
|
||||
fi
|
||||
}
|
||||
|
||||
_jaildk_completions_build() {
|
||||
if [[ ${cur:0:1} == [-+] ]]; then
|
||||
# rely options of command
|
||||
_jaildk_comp_reply_list _jaildk_comp_subcmd_opts_build
|
||||
elif [[ ${prev:0:1} == [-+] ]]; then
|
||||
case "${prev}" in
|
||||
# rely the value of command option
|
||||
-b) _jaildk_comp_reply_base ;;
|
||||
-v) _jaildk_comp_reply_version ;;
|
||||
-m) _jaildk_comp_reply_words 'start,stop,status,restart' ;;
|
||||
*) _jaildk_comp_reply_jail ;;
|
||||
esac
|
||||
else
|
||||
# rely the argument of command
|
||||
_jaildk_comp_reply_jail
|
||||
fi
|
||||
}
|
||||
|
||||
_jaildk_completions_clone() {
|
||||
if [[ ${cur:0:1} == [-+] ]]; then
|
||||
# rely options of command
|
||||
_jaildk_comp_reply_list _jaildk_comp_subcmd_opts_clone
|
||||
elif [[ ${prev:0:1} == [-+] ]]; then
|
||||
case "${prev}" in
|
||||
# rely the value of command option
|
||||
-s) _jaildk_comp_reply_jail ;;
|
||||
-d) _jaildk_comp_reply_jail ;;
|
||||
-o) _jaildk_comp_reply_version ;;
|
||||
-n) _jaildk_comp_reply_version ;;
|
||||
*) _jaildk_comp_reply_files ;;
|
||||
esac
|
||||
else
|
||||
# rely the argument of command
|
||||
_jaildk_comp_reply_files
|
||||
fi
|
||||
}
|
||||
|
||||
_jaildk_completions_fetchports() {
|
||||
if [[ ${cur:0:1} == [-+] ]]; then
|
||||
# rely options of command
|
||||
_jaildk_comp_reply_list _jaildk_comp_subcmd_opts_fetchports
|
||||
elif [[ ${prev:0:1} == [-+] ]]; then
|
||||
case "${prev}" in
|
||||
# rely the value of command option
|
||||
-v) _jaildk_comp_reply_version ;;
|
||||
*) _jaildk_comp_reply_files ;;
|
||||
esac
|
||||
else
|
||||
# rely the argument of command
|
||||
_jaildk_comp_reply_files
|
||||
fi
|
||||
}
|
||||
|
||||
_jaildk_completions_install() {
|
||||
if [[ ${cur:0:1} == [-+] ]]; then
|
||||
# rely options of command
|
||||
_jaildk_comp_reply_list _jaildk_comp_subcmd_opts_install
|
||||
elif [[ ${prev:0:1} == [-+] ]]; then
|
||||
case "${prev}" in
|
||||
# rely the value of command option
|
||||
-m) _jaildk_comp_reply_words 'start,stop,status,restart' ;;
|
||||
-r) _jaildk_comp_reply_words 'mount,ports,mtree,pf' ;;
|
||||
*) _jaildk_comp_reply_jail ;;
|
||||
esac
|
||||
else
|
||||
# rely the argument of command
|
||||
_jaildk_comp_reply_jail
|
||||
fi
|
||||
}
|
||||
|
||||
_jaildk_completions_uninstall() {
|
||||
if [[ ${cur:0:1} == [-+] ]]; then
|
||||
# rely options of command
|
||||
_jaildk_comp_reply_list _jaildk_comp_subcmd_opts_uninstall
|
||||
elif [[ ${prev:0:1} == [-+] ]]; then
|
||||
case "${prev}" in
|
||||
# rely the value of command option
|
||||
*) _jaildk_comp_reply_jail ;;
|
||||
esac
|
||||
else
|
||||
# rely the argument of command
|
||||
_jaildk_comp_reply_jail
|
||||
fi
|
||||
}
|
||||
|
||||
_jaildk_completions_reinstall() {
|
||||
if [[ ${cur:0:1} == [-+] ]]; then
|
||||
# rely options of command
|
||||
_jaildk_comp_reply_list _jaildk_comp_subcmd_opts_reinstall
|
||||
elif [[ ${prev:0:1} == [-+] ]]; then
|
||||
case "${prev}" in
|
||||
# rely the value of command option
|
||||
-b) _jaildk_comp_reply_base ;;
|
||||
-v) _jaildk_comp_reply_version ;;
|
||||
*) _jaildk_comp_reply_jail ;;
|
||||
esac
|
||||
else
|
||||
# rely the argument of command
|
||||
_jaildk_comp_reply_jail
|
||||
fi
|
||||
}
|
||||
|
||||
_jaildk_completions_prune() {
|
||||
if [[ ${cur:0:1} == [-+] ]]; then
|
||||
# rely options of command
|
||||
_jaildk_comp_reply_list _jaildk_comp_subcmd_opts_prune
|
||||
elif [[ ${prev:0:1} == [-+] ]]; then
|
||||
case "${prev}" in
|
||||
# rely the value of command option
|
||||
-j) _jaildk_comp_reply_jail ;;
|
||||
*) _jaildk_comp_reply_files ;;
|
||||
esac
|
||||
else
|
||||
# rely the argument of command
|
||||
_jaildk_comp_reply_files
|
||||
fi
|
||||
}
|
||||
|
||||
_jaildk_completions_status() {
|
||||
if [[ ${cur:0:1} == [-+] ]]; then
|
||||
# rely options of command
|
||||
_jaildk_comp_reply_list _jaildk_comp_subcmd_opts_status
|
||||
elif [[ ${prev:0:1} == [-+] ]]; then
|
||||
case "${prev}" in
|
||||
# rely the value of command option
|
||||
*) _jaildk_comp_reply_jail ;;
|
||||
esac
|
||||
else
|
||||
# rely the argument of command
|
||||
_jaildk_comp_reply_jail
|
||||
fi
|
||||
}
|
||||
|
||||
_jaildk_completions_rc() {
|
||||
if [[ ${cur:0:1} == [-+] ]]; then
|
||||
# rely options of command
|
||||
_jaildk_comp_reply_list _jaildk_comp_subcmd_opts_rc
|
||||
elif [[ ${prev:0:1} == [-+] ]]; then
|
||||
case "${prev}" in
|
||||
# rely the value of command option
|
||||
-m) _jaildk_comp_reply_words 'start,stop,status,restart' ;;
|
||||
-r) _jaildk_comp_reply_words 'mount,ports,mtree,pf' ;;
|
||||
*) _jaildk_comp_reply_files ;;
|
||||
esac
|
||||
else
|
||||
# rely the argument of command
|
||||
_jaildk_comp_reply_files
|
||||
fi
|
||||
}
|
||||
|
||||
_jaildk_completions_ipfw() {
|
||||
if [[ ${cur:0:1} == [-+] ]]; then
|
||||
# rely options of command
|
||||
_jaildk_comp_reply_list _jaildk_comp_subcmd_opts_ipfw
|
||||
elif [[ ${prev:0:1} == [-+] ]]; then
|
||||
case "${prev}" in
|
||||
# rely the value of command option
|
||||
-m) _jaildk_comp_reply_words 'start,stop,status,restart' ;;
|
||||
*) _jaildk_comp_reply_jail ;;
|
||||
esac
|
||||
else
|
||||
# rely the argument of command
|
||||
_jaildk_comp_reply_jail
|
||||
fi
|
||||
}
|
||||
|
||||
_jaildk_completions_freeze() {
|
||||
if [[ ${cur:0:1} == [-+] ]]; then
|
||||
# rely options of command
|
||||
_jaildk_comp_reply_list _jaildk_comp_subcmd_opts_freeze
|
||||
elif [[ ${prev:0:1} == [-+] ]]; then
|
||||
case "${prev}" in
|
||||
# rely the value of command option
|
||||
-v) _jaildk_comp_reply_version ;;
|
||||
*) _jaildk_comp_reply_jail ;;
|
||||
esac
|
||||
else
|
||||
# rely the argument of command
|
||||
_jaildk_comp_reply_jail
|
||||
fi
|
||||
}
|
||||
|
||||
_jaildk_completions_update() {
|
||||
if [[ ${cur:0:1} == [-+] ]]; then
|
||||
# rely options of command
|
||||
_jaildk_comp_reply_list _jaildk_comp_subcmd_opts_update
|
||||
elif [[ ${prev:0:1} == [-+] ]]; then
|
||||
case "${prev}" in
|
||||
# rely the value of command option
|
||||
*) _jaildk_comp_reply_files ;;
|
||||
esac
|
||||
else
|
||||
# rely the argument of command
|
||||
_jaildk_comp_reply_files
|
||||
fi
|
||||
}
|
||||
|
||||
_jaildk_completions__fallback() {
|
||||
if [[ ${cur:0:1} == [-+] ]]; then
|
||||
# rely options of command
|
||||
_jaildk_comp_reply_list _jaildk_comp_subcmd_opts__fallback
|
||||
elif [[ ${prev:0:1} == [-+] ]]; then
|
||||
case "${prev}" in
|
||||
# rely the value of command option
|
||||
*) _jaildk_comp_reply_files ;;
|
||||
esac
|
||||
else
|
||||
# rely the argument of command
|
||||
_jaildk_comp_reply_files
|
||||
fi
|
||||
}
|
||||
|
||||
_jaildk_completions() {
|
||||
COMPREPLY=()
|
||||
local cur=${COMP_WORDS[COMP_CWORD]}
|
||||
local prev=${COMP_WORDS[COMP_CWORD-1]}
|
||||
|
||||
# Uncomment this line for debug
|
||||
# echo "[COMP_CWORD:$COMP_CWORD][cur:$cur][prev:$prev][WORD_COUNT:${#COMP_WORDS[*]}][COMP_WORDS:${COMP_WORDS[*]}]" >> bash-debug.log
|
||||
|
||||
if (( COMP_CWORD > 1 )); then
|
||||
# Enter the subcmd completion
|
||||
local subcmd_varname
|
||||
subcmd_varname="$(_jaildk_comp_util_get_varname "${COMP_WORDS[1]}")"
|
||||
if type "_jaildk_completions_$subcmd_varname" &>/dev/null; then
|
||||
"_jaildk_completions_$subcmd_varname"
|
||||
else
|
||||
# If subcmd completion function not defined, use the fallback
|
||||
"_jaildk_completions__fallback"
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Enter the cmd completion
|
||||
if [[ ${cur:0:1} == [-+] ]]; then
|
||||
# rely options of command
|
||||
_jaildk_comp_reply_list _jaildk_comp_cmd_opts
|
||||
elif [[ ${prev:0:1} == [-+] ]]; then
|
||||
case "${prev}" in
|
||||
# rely the value of command option
|
||||
*) _jaildk_comp_reply_files ;;
|
||||
esac
|
||||
else
|
||||
# rely the argument of command
|
||||
_jaildk_comp_reply_list _jaildk_comp_subcmds
|
||||
fi
|
||||
}
|
||||
|
||||
complete -F _jaildk_completions -o bashdefault jaildk
|
||||
# vi: sw=2 ts=2
|
||||
Reference in New Issue
Block a user