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\ncomplete\n"
- sed -ne "s/$gsu_command_regex/\1/g;T;p" $0
- } | sort | tr '\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()
{(
- local i count
+ local i count=0
gsu_short_msg "Available commands:"
for i in $gsu_cmds; do
printf "$i"
gsu_complete_options()
{
- local opts="$1" cword="$2" cur
+ local opts="$1" cword="$2" cur opt
local -a words
shift 2
{
local i conf="${gsu_config_file:=$HOME/.$gsu_name.rc}"
- if [[ "$1" = "-e" ]]; then
+ gsu_getopts "e"
+ eval "$result"
+ (($ret < 0)) && return
+ gsu_check_arg_count $# 0 0
+ (($ret < 0)) && return
+
+ if [[ "$o_e" == "true" ]]; then
ret=-$E_GSU_MKDIR
result="${conf%/*}"
mkdir -p "$result"
local name= option_type= default_value= required=
local description= help_text=
eval "${gsu_options[$i]}"
- eval val='"$'${gsu_config_var_prefix}_$name'"'
+ eval val='"${'${gsu_config_var_prefix}_$name:-'}"'
case "$required" in
true|yes)
printf "# required"
_gsu_banner_msg()
{
- local txt="### $_gsu_self --"
- if test -z "$gsu_banner_txt"; then
- txt="$txt set \$gsu_banner_txt to customize this message"
- else
- txt="$txt $gsu_banner_txt"
- fi
- gsu_short_msg "$txt ###"
+ gsu_short_msg "### $_gsu_self -- ###"
}
export gsu_help_txt="
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.
+When executed without argument the command writes bash code to
+stdout. This code is suitable to be evaled from .bashrc to enable
+completion.
-Completion code suitable to be evaled is written to stdout if no argument
-was given.
+If at least one argument is given, all possible completions are
+written to stdout. This can be used from the completion function of
+the subcommand.
"
_com_help()
{
- local a b
- if test -z "$1"; then
+ local a b ere tab=' '
+
+ _gsu_get_command_regex
+ ere="$result"
+
+ if (($# == 0)); then
_gsu_banner_msg 2>&1
_gsu_usage 2>&1
{
printf "com_man()\n$gsu_man_txt" | head -n 4; echo "--"
printf "com_prefs()\n$gsu_prefs_txt" | head -n 4; echo "--"
printf "com_complete()\n$gsu_complete_txt" | head -n 4; echo "--"
- grep -A 2 "$gsu_command_regex" $0
+ 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
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()
echo "$result"
}
-# Wrapper for bash's getopts.
+# Wrapper for the bash getopts builtin.
#
# Aborts on programming errors such as missing or invalid option string. On
# success $result contains shell code that can be eval'ed. For each defined
# option x, the local variable o_x will be created when calling eval "$result".
-# o_x contains true/false for options without an argument or the emtpy string/the
-# given argument, depending on whether this option was contained in the "$@"
-# array.
+# o_x contains true/false for options without argument and either the emtpy
+# string or the given argument for options that take an argument.
#
# Example:
# gsu_getopts abc:x:y
# eval "$result"
-# [[ $ret -lt 0 ]] && return
+# (($ret < 0)) && return
#
-# [[ "$o_a" = "true ]] && echo "The -a flag was given"
+# [[ "$o_a" = 'true' ]] && echo 'The -a flag was given'
# [[ -n "$o_c" ]] && echo "The -c option was given with arg $o_c"
gsu_getopts()
{
exit 1
esac
done
- result="local opt"
+ result="local _gsu_getopts_opt"
for ((i=0; i < ${#1}; i++)); do
c1=${1:$i:1}
c2=${1:$(($i + 1)):1}
- result+=" o_$c1"
+ result+=" o_$c1="
if [[ "$c2" = ":" ]]; then
let i++
else
- result+="=false"
+ result+="false"
fi
done
result+="
OPTIND=1
- while getopts $1 opt \"\$@\"; do
- case \"\$opt\" in
+ while getopts $1 _gsu_getopts_opt \"\$@\"; do
+ case \"\$_gsu_getopts_opt\" in
"
for ((i=0; i < ${#1}; i++)); do
c1=${1:$i:1}
_com_complete()
{
- local cmd n cword="$1"
+ local cmd n cword
local -a words
if (($# == 0)); then
candidates=(\$($0 complete "\$COMP_CWORD" "\${COMP_WORDS[@]}"));
COMPREPLY=(\$(compgen -W "\${candidates[*]}" -- "\$cur"));
EOF
+ ret=$GSU_SUCCESS
+ return
fi
- [[ -z "$cword" ]] && return
+ cword="$1"
+ gsu_is_a_number "$cword"
+ (($ret < 0)) && return
if (($cword <= 1)); then
_gsu_available_commands
echo "${result}"
ret=$GSU_SUCCESS
}
+# Find out if the current word is a parameter for an option.
+#
+# $1: usual getopts option string.
+# $2: The current word number.
+# $3..: All words of the current command line.
+#
+# return: If yes, $result contains the letter of the option for which the
+# current word is a parameter. Otherwise, $result is empty.
+#
gsu_cword_is_option_parameter()
{
local opts="$1" cword="$2" prev i n
[[ ! "$prev" == -* ]] && return
n=$((${#opts} - 1))
- for ((i=0; i < $n; i++)); do
+ for ((i=0; i <= $n; i++)); do
opt="${opts:$i:1}"
[[ "${opts:$(($i + 1)):1}" != ":" ]] && continue
let i++
ret=0
}
+# Get the word number on which the cursor is, not counting options.
+#
+# This is useful for completing commands whose possible completions depend
+# on the word number, for example mount.
+#
+# $1: Getopt option string.
+# $2: The current word number.
+# $3..: All words of the current command line.
+#
+# return: If the current word is an option, or a parameter to an option,
+# this function sets $result to -1. Otherwise, the number of the non-option
+# is returned in $result.
+#
+gsu_get_unnamed_arg_num()
+{
+ local opts="$1" cword="$2" prev cur
+ local -i i n=0
+ local -a words
+
+ shift 2
+ words=("$@")
+ cur="${words[$cword]}"
+ prev="${words[$(($cword - 1))]}"
+ result=-1
+ [[ "$cur" == -* ]] && return
+ [[ "$prev" == -* ]] && [[ "$opts" == *${prev#-}:* ]] && return
+
+ for ((i=1; i <= $cword; i++)); do
+ prev="${words[$(($i - 1))]}"
+ cur="${words[$i]}"
+ [[ "$cur" == -* ]] && continue
+ if [[ "$prev" == -* ]]; then
+ opt=${prev#-}
+ [[ "$opts" != *$opt:* ]] && let n++
+ continue
+ fi
+ let n++
+ done
+ result="$(($n - 1))"
+}
+
gsu()
{
local i