gsu_short_msg "# Usage: $_gsu_self command [options]"
}
-# Each line matching this is recognized as a subcommand. The name
-# of the subcommand is the first subexpression.
-export gsu_command_regex='^com_\([-a-zA-Z_0-9]\+\)()'
+# Each line matching this is recognized as a subcommand. The name of the may be
+# given as $1. In any case the subcommand is the first subexpression.
+_gsu_get_command_regex()
+{
+ local cmd="${1:-[-a-zA-Z_0-9]+}"
+ result="^com_($cmd)\(\)"
+}
_gsu_available_commands()
{
+ local ere
+
+ _gsu_get_command_regex
+ ere="$result"
result="$({
- printf "help\nman\nprefs\n"
- sed -ne "s/$gsu_command_regex/\1/g;T;p" $0
- } | sort | tr '\n' ' ')"
+ printf "help\nman\nprefs\ncomplete\n"
+ sed -Ee '
+ # if line matches, isolate command name
+ s/'"$ere"'/\1/g
+
+ # if there is a match, (print it and) start next cycle
+ t
+
+ # otherwise delete it
+ d
+ ' $0
+ } | sort | tr '\n' ' ')"
}
_gsu_print_available_commands()
) 2>&1
}
+gsu_complete_options()
+{
+ local opts="$1" cword="$2" cur
+ local -a words
+
+ shift 2
+ words=("$@")
+ cur="${words[$cword]}"
+ ret=0
+ [[ ! "$cur" == -* ]] && return
+
+ ret=0
+ for ((i=0; i < ${#opts}; i++)); do
+ opt="${opts:$i:1}"
+ [[ "$opt" == ":" ]] && continue
+ printf "%s" "-$opt "
+ let ret++
+ done
+}
+
export gsu_prefs_txt="
Print the current preferences.
done
}
+complete_prefs()
+{
+ gsu_complete_options "e" "$@"
+}
+
export gsu_man_txt="
Print the manual.
Without arguments, print the list of available commands. Otherwise,
print the help text for the given command."
+export gsu_complete_txt="
+Command line completion.
+
+Usage: complete [<cword> <word>...]
+
+In the first form, the command prints all possible completions to stdout.
+This can be used from the completion function of the shell.
+
+Completion code suitable to be evaled is written to stdout if no argument
+was given.
+"
+
_com_help()
{
- local a b
+ local a b ere tab=' '
+
+ _gsu_get_command_regex
+ ere="$result"
+
if test -z "$1"; then
_gsu_banner_msg 2>&1
_gsu_usage 2>&1
printf "com_help()\n$gsu_help_txt" | head -n 4; echo "--"
printf "com_man()\n$gsu_man_txt" | head -n 4; echo "--"
printf "com_prefs()\n$gsu_prefs_txt" | head -n 4; echo "--"
- grep -A 2 "$gsu_command_regex" $0
+ printf "com_complete()\n$gsu_complete_txt" | head -n 4; echo "--"
+ grep -EA 2 "$ere" $0
} | grep -v -- '--' \
- | sed -e "/$gsu_command_regex/bs" \
- -e 'H;$!d;x;s/\n//g;b' \
- -e :s \
- -e 'x;s/\n//g;${p;x;}' \
- | sed -e "s/${gsu_command_regex}#*/\1\t/" \
- | sort \
- | while read a b; do
- printf "$a\t"
- if test ${#a} -lt 8; then
- printf "\t"
- fi
- echo "$b"
- done
+ | sed -En "/$ere/"'!d
+ # remove everything but the command name
+ s/^com_(.*)\(\).*/\1/
+
+ # append tab after short commands (less than 8 chars)
+ s/^(.{1,7})$/\1'"$tab"'/g
+
+ # remove next line (should contain only ## anyway)
+ N
+ s/#.*//
+
+ # append next line, removing leading ##
+ N
+ s/#+ *//g
+
+ # replace newline by tab
+ y/\n/'"$tab"'/
+
+ # and print the sucker
+ p'
echo
echo "# Try $_gsu_self help <command> for info on <command>."
ret=$GSU_SUCCESS
ret=$GSU_SUCCESS
return
fi
+ if test "$1" = "complete"; then
+ echo "$gsu_complete_txt"
+ ret=$GSU_SUCCESS
+ return
+ fi
ret=$GSU_SUCCESS
- if grep -q "^com_$1()" $0; then
- sed -e "1,/^com_$1()$/d" -e '/^{/,$d' -e 's/^## *//' $0
+ _gsu_get_command_regex "$1"
+ ere="$result"
+ if ! grep -Eq "$ere" $0; then
+ _gsu_print_available_commands
+ result="$1"
+ ret=-$E_GSU_BAD_COMMAND
return
fi
- _gsu_print_available_commands
- result="$1"
- ret=-$E_GSU_BAD_COMMAND
+ sed -nEe '
+ # only consider lines in the comment of the function
+ /'"$ere"'/,/^[^#]/ {
+
+ # remove leading ##
+ s/^## *//
+
+ # if it did start with ##, jump to label p and print it
+ tp
+
+ # otherwise, move on to next line
+ d
+
+ # print it
+ :p
+ p
+ }
+ ' $0
+}
+
+complete_help()
+{
+ _gsu_available_commands
+ echo "$result"
}
# Wrapper for bash's getopts.
ret=$GSU_SUCCESS
}
+_com_complete()
+{
+ local cmd n cword="$1"
+ local -a words
+
+ if (($# == 0)); then
+ cat <<EOF
+ local cur="\${COMP_WORDS[\$COMP_CWORD]}";
+ local -a candidates;
+
+ candidates=(\$($0 complete "\$COMP_CWORD" "\${COMP_WORDS[@]}"));
+ COMPREPLY=(\$(compgen -W "\${candidates[*]}" -- "\$cur"));
+EOF
+ fi
+
+ [[ -z "$cword" ]] && return
+ if (($cword <= 1)); then
+ _gsu_available_commands
+ echo "${result}"
+ ret=$GSU_SUCCESS
+ return
+ fi
+ shift
+ words=("$@")
+ cmd="${words[1]}"
+ ret=$GSU_SUCCESS # It's not an error if no completer was defined
+ [[ "$(type -t complete_$cmd)" != "function" ]] && return
+ complete_$cmd "$cword" "${words[@]}"
+ # ignore errors, they would only clutter the completion output
+ ret=$GSU_SUCCESS
+}
+
+gsu_cword_is_option_parameter()
+{
+ local opts="$1" cword="$2" prev i n
+ local -a words
+
+ result=
+ (($cword == 0)) && return
+ ((${#opts} < 2)) && return
+
+ shift 2
+ words=("$@")
+ prev="${words[$(($cword - 1))]}"
+ [[ ! "$prev" == -* ]] && return
+
+ n=$((${#opts} - 1))
+ for ((i=0; i < $n; i++)); do
+ opt="${opts:$i:1}"
+ [[ "${opts:$(($i + 1)):1}" != ":" ]] && continue
+ let i++
+ [[ "$prev" != "-$opt" ]] && continue
+ result="$opt"
+ return
+ done
+ ret=0
+}
+
gsu()
{
local i
arg="$1"
shift
# check internal commands
- if [[ "$arg" = "help" || "$arg" = "man" || "$arg" = "prefs" ]]; then
+ if [[ "$arg" = "help" || "$arg" = "man" || "$arg" = "prefs" || "$arg" = "complete" ]]; then
_com_$arg "$@"
if [[ "$ret" -lt 0 ]]; then
gsu_err_msg