README: Mention that the gui example script must be named "lsi".
[gsu.git] / subcommand
index a07f4145c69adb6fa319dbdaa9c97e3b0a2d5cde..d37481cdbdccb5b62268d3b788de84b61a2ab347 100644 (file)
@@ -96,11 +96,12 @@ gsu_check_arg_count()
 #      [[ -n "$o_c" ]] && echo "The -c option was given with arg $o_c"
 gsu_getopts()
 {
-       local i c tab=' ' cr='
+       local -i i
+       local c c1 c2 tab='     ' cr='
 '
 
        gsu_check_arg_count $# 1 1
-       if (($ret < 0)); then
+       if ((ret < 0)); then
                gsu_err_msg
                exit 1
        fi
@@ -126,7 +127,7 @@ gsu_getopts()
        result="local _gsu_getopts_opt"
        for ((i=0; i < ${#1}; i++)); do
                c1=${1:$i:1}
-               c2=${1:$(($i + 1)):1}
+               c2=${1:$((i + 1)):1}
                result+=" o_$c1="
                if [[ "$c2" = ":" ]]; then
                        let i++
@@ -141,7 +142,7 @@ gsu_getopts()
 "
        for ((i=0; i < ${#1}; i++)); do
                c1=${1:$i:1}
-               c2=${1:$(($i + 1)):1}
+               c2=${1:$((i + 1)):1}
                result+="$tab$tab$c1) o_$c1="
                if [[ "$c2" = ":" ]]; then
                        result+="\"\$OPTARG\""
@@ -167,26 +168,33 @@ gsu_getopts()
 _gsu_print_available_commands()
 {
        local cmd cmds
-       local -i count=0
+       local -i maxlen=0 cols width=80 count=0
 
+       result=$(stty size 2>/dev/null)
+       if (($? == 0)); then
+               gsu_is_a_number "${result#* }"
+               ((ret >= 0)) && ((result > 0)) && width=$result
+       fi
        _gsu_available_commands
-       cmds="$result"
-       printf 'Available commands:\n'
+       cmds=$result
+       for cmd in $cmds; do
+               ((${#cmd} > maxlen)) && maxlen=${#cmd}
+       done
+       let maxlen++
+       ((width < maxlen)) && cols=1 || cols=$((width / maxlen))
+       printf 'Available commands:'
        for cmd in $cmds; do
-               printf '%s' "$cmd"
+               ((count % cols == 0)) && printf '\n'
+               printf '%-*s' $maxlen $cmd
                let ++count
-               if (($count % 4)); then
-                       printf '\t'
-                       ((${#cmd} < 8)) && printf '\t'
-               else
-                       printf '\n'
-               fi
        done
        printf '\n'
 }
 
 # Print all options of the given optstring to stdout if the word in the current
 # command line begins with a hyphen character.
+#
+# Returns 0 if the current word does not start with a hyphen, one otherwise.
 gsu_complete_options()
 {
        local opts="$1" cword="$2" cur opt
@@ -198,18 +206,18 @@ gsu_complete_options()
        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
+       ret=1
 }
 
+declare -A _gsu_help_text=() # indexed by autocmd
 com_prefs_options='e'
 
-_gsu_prefs_txt="
+_gsu_help_text['prefs']='
 Print the current preferences.
 
 Usage: prefs [-e]
@@ -217,7 +225,7 @@ Usage: prefs [-e]
 If -e is given, the config file is opened with the default editor.
 Without options, the command prints out a list of all config variables,
 together with their current value and the default value.
-"
+'
 
 com_prefs()
 {
@@ -225,9 +233,9 @@ com_prefs()
 
        gsu_getopts "$com_prefs_options"
        eval "$result"
-       (($ret < 0)) && return
+       ((ret < 0)) && return
        gsu_check_arg_count $# 0 0
-       (($ret < 0)) && return
+       ((ret < 0)) && return
 
        if [[ "$o_e" == "true" ]]; then
                ret=-$E_GSU_MKDIR
@@ -236,7 +244,7 @@ com_prefs()
                (($? != 0)) && return
                ret=-$E_GSU_EDITOR
                result="${EDITOR:-vi}"
-               "$result" "$conf"
+               "$result" $conf
                (($? != 0)) && return
                ret=$GSU_SUCCESS
                return
@@ -255,7 +263,7 @@ com_prefs()
                        printf "# optional"
                        ;;
                esac
-               printf "%s: %s" "$option_type" "$description"
+               printf " %s: %s" "$option_type" "$description"
                if [[ "$required" != "yes" && "$required" != "true" ]]; then
                        printf " [%s]" "$default_value"
                fi
@@ -288,12 +296,12 @@ complete_man()
        [[ "$result" == 'm' ]] && printf 'roff\ntext\nhtml\n'
 }
 
-_gsu_man_txt='
+_gsu_help_text['man']='
 Print the manual.
 
 Usage: man [-m <mode>] [-b <browser>]
 
--m: Set output format (text, roff or html). Default: text.
+-m: Set output format (text, roff or html). Default: roff.
 -b: Use the specified browser. Implies html mode.
 
 If stdout is not associated with a terminal device, the command
@@ -309,9 +317,6 @@ file is displayed as a page in the web browser. If -b is not given,
 the command stored in the $BROWSER environment variable is executed
 with the path to the temporary file as an argument. If $BROWSER is
 unset, elinks(1) is assumed.
-
-It is recommended to specify the output format with -m as the default
-mode might change in future versions of gsu.
 '
 
 _gsu_read_line()
@@ -376,7 +381,7 @@ _gsu_roffify_maindoc()
                fi
                if [[ "${line:0:1}" == "$TAB" ]]; then # example
                        _gsu_change_roffify_state 'state' 'example'
-                       printf '%s\n' "$line"
+                       _gsu_print_protected_roff_line "$line"
                        line="$next_line"
                        continue
                fi
@@ -453,14 +458,17 @@ _gsu_roffify_cmds()
        done
 }
 
-_gsu_roffify_autocmd()
+_gsu_roffify_autocmds()
 {
-       local cmd="$1" help_txt="$2"
+       local cmd help_txt
 
-       {
-               printf 'com_%s()\n' "$cmd"
-               sed -e 's/^/## /g' <<< "$help_txt"
-       } | _gsu_roffify_cmds
+       for cmd in "${!_gsu_help_text[@]}"; do
+               help_txt="${_gsu_help_text["$cmd"]}"
+               {
+                       printf 'com_%s()\n' "$cmd"
+                       sed -e 's/^/## /g' <<< "$help_txt"
+               } | _gsu_roffify_cmds
+       done
 }
 
 _gsu_roff_man()
@@ -482,10 +490,7 @@ EOF
 
        printf '\n.SH "GENERIC SUBCOMMANDS"\n'
        printf 'The following commands are automatically created by gsu\n'
-       _gsu_roffify_autocmd "help" "$_gsu_help_txt"
-       _gsu_roffify_autocmd "man" "$_gsu_man_txt"
-       _gsu_roffify_autocmd "prefs" "$_gsu_prefs_txt"
-       _gsu_roffify_autocmd "complete" "$_gsu_complete_txt"
+       _gsu_roffify_autocmds
 
        printf '\n.SH "LIST OF SUBCOMMANDS"\n'
        printf 'Each command has its own set of options as described below.\n'
@@ -522,7 +527,7 @@ com_man()
                o_m='html'
                browser="$o_b"
        elif [[ -z "$o_m" ]]; then
-               o_m='text'
+               o_m='roff'
        fi
 
        _gsu_isatty && isatty='true' || isatty='false'
@@ -597,7 +602,7 @@ com_man()
        _gsu_available_commands
        for com in $result; do
                num=${#com}
-               (($num < 4)) && num=4
+               ((num < 4)) && num=4
                echo "${minus_signs:0:$num}"
                echo "$com"
                echo "${minus_signs:0:$num}"
@@ -608,15 +613,18 @@ com_man()
        ret=$GSU_SUCCESS
 }
 
-_gsu_help_txt="
+_gsu_help_text['help']='
 Print online help.
 
-Usage: help [command]
+Usage: help [-a] [command]
 
 Without arguments, print the list of available commands. Otherwise,
-print the help text for the given command."
+print the help text for the given command.
+
+-a: Also show the help of automatic commands. Ignored if a command
+is given.'
 
-_gsu_complete_txt="
+_gsu_help_text['complete']='
 Command line completion.
 
 Usage: complete [<cword> <word>...]
@@ -628,11 +636,16 @@ completion.
 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_options='a'
 com_help()
 {
-       local ere tab=' '
+       local ere tab=' ' txt
+
+       gsu_getopts "$com_help_options"
+       eval "$result"
+       ((ret < 0)) && return
 
        _gsu_get_command_regex
        ere="$result"
@@ -641,10 +654,15 @@ com_help()
                gsu_short_msg "### $gsu_name -- $gsu_banner_txt ###"
                _gsu_usage 2>&1
                {
-                       printf "com_help()\n%s" "$_gsu_help_txt" | head -n 4; echo "--"
-                       printf "com_man()\n%s" "$_gsu_man_txt" | head -n 4; echo "--"
-                       printf "com_prefs()\n%s" "$_gsu_prefs_txt" | head -n 4; echo "--"
-                       printf "com_complete()\n%s" "$_gsu_complete_txt" | head -n 4; echo "--"
+                       if [[ "$o_a" == 'true' ]]; then
+                               _gsu_mfcb() { printf '%s\n' "$2"; }
+                               for cmd in "${!_gsu_help_text[@]}"; do
+                                       printf "com_%s()" "$cmd"
+                                       txt="${_gsu_help_text["$cmd"]}"
+                                       mapfile -n 3 -c 1 -C _gsu_mfcb <<< "$txt"
+                               printf -- '--\n'
+                               done
+                       fi
                        grep -EA 2 "$ere" "$0"
                } | grep -v -- '--' \
                        | sed -En "/$ere/"'!d
@@ -666,33 +684,33 @@ com_help()
                                y/\n/'"$tab"'/
 
                                # and print the sucker
-                               p'
-               echo
-               echo "# Try $gsu_name help <command> for info on <command>."
-               ret=$GSU_SUCCESS
-               return
-       fi
-       if test "$1" = "help"; then
-               echo "$_gsu_help_txt"
-               ret=$GSU_SUCCESS
-               return
-       fi
-       if test "$1" = "man"; then
-               echo "$_gsu_man_txt"
+                               p
+                       ' | {
+                               local -a cmds=() descs=()
+                               local -i i maxlen=1
+                               local cmd desc
+                               while read cmd desc; do
+                                       ((maxlen < ${#cmd})) && maxlen=${#cmd}
+                                       cmds[${#cmds[@]}]=$cmd
+                                       descs[${#descs[@]}]=$desc
+                               done
+                               for ((i = 0; i < ${#cmds[@]}; i++)); do
+                                       printf '%-*s %s\n' $maxlen ${cmds[$i]} \
+                                               "${descs[$i]}"
+                               done
+                       }
+               printf "\n# Try %s help <command> for info on <command>, or %s help -a to see\n" \
+                       "$gsu_name" "$gsu_name"
+               printf '# also the subcommands which are automatically generated by gsu.\n'
                ret=$GSU_SUCCESS
                return
        fi
-       if test "$1" = "prefs"; then
-               echo "$_gsu_prefs_txt"
+       for cmd in "${!_gsu_help_text[@]}"; do
+               [[ "$1" != "$cmd" ]] && continue
+               printf '%s\n' "${_gsu_help_text["$cmd"]}"
                ret=$GSU_SUCCESS
                return
-       fi
-       if test "$1" = "complete"; then
-               echo "$_gsu_complete_txt"
-               ret=$GSU_SUCCESS
-               return
-       fi
-       ret=$GSU_SUCCESS
+       done
        _gsu_get_command_regex "$1"
        ere="$result"
        if ! grep -Eq "$ere" "$0"; then
@@ -719,6 +737,7 @@ com_help()
                        p
                }
        ' "$0"
+       ret=$GSU_SUCCESS
 }
 
 complete_help()
@@ -738,7 +757,12 @@ com_complete()
                local -a candidates;
 
                candidates=(\$($0 complete "\$COMP_CWORD" "\${COMP_WORDS[@]}"));
-               COMPREPLY=(\$(compgen -W "\${candidates[*]}" -- "\$cur"));
+               if ((\$? == 0)); then
+                       COMPREPLY=(\$(compgen -W "\${candidates[*]}" -- "\$cur"));
+               else
+                       compopt -o filenames;
+                       COMPREPLY=(\$(compgen -fd -- "\$cur"));
+               fi
 EOF
                ret=$GSU_SUCCESS
                return
@@ -746,8 +770,8 @@ EOF
 
        cword="$1"
        gsu_is_a_number "$cword"
-       (($ret < 0)) && return
-       if (($cword <= 1)); then
+       ((ret < 0)) && return
+       if ((cword <= 1)); then
                _gsu_available_commands
                echo "${result}"
                ret=$GSU_SUCCESS
@@ -756,8 +780,9 @@ EOF
        shift
        words=("$@")
        cmd="${words[1]}"
-       ret=$GSU_SUCCESS # It's not an error if no completer was defined
-       [[ "$(type -t "complete_$cmd")" != "function" ]] && return
+       # if no completer is defined for this subcommand we exit unsuccessfully
+       # to let the generic completer above fall back to file name completion.
+       [[ "$(type -t "complete_$cmd")" != "function" ]] && exit 1
        "complete_$cmd" "$cword" "${words[@]}"
        # ignore errors, they would only clutter the completion output
        ret=$GSU_SUCCESS
@@ -779,18 +804,18 @@ gsu_cword_is_option_parameter()
        local -a words
 
        result=
-       (($cword == 0)) && return
+       ((cword == 0)) && return
        ((${#opts} < 2)) && return
 
        shift 2
        words=("$@")
-       prev="${words[$(($cword - 1))]}"
+       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
+               [[ "${opts:$((i + 1)):1}" != ":" ]] && continue
                let i++
                [[ ! "$prev" =~ ^-.*$opt$ ]] && continue
                result="$opt"
@@ -821,13 +846,13 @@ gsu_get_unnamed_arg_num()
        shift 2
        words=("$@")
        cur="${words[$cword]}"
-       prev="${words[$(($cword - 1))]}"
+       prev="${words[$((cword - 1))]}"
        result=-1
        [[ "$cur" == -* ]] && return
        [[ "$prev" == -* ]] && [[ "$opts" == *${prev#-}:* ]] && return
 
        for ((i=1; i <= $cword; i++)); do
-               prev="${words[$(($i - 1))]}"
+               prev="${words[$((i - 1))]}"
                cur="${words[$i]}"
                [[ "$cur" == -* ]] && continue
                if [[ "$prev" == -* ]]; then
@@ -837,7 +862,7 @@ gsu_get_unnamed_arg_num()
                fi
                let n++
        done
-       result="$(($n - 1))"
+       result="$((n - 1))"
 }
 
 # Entry point for all gsu-based scripts.
@@ -868,7 +893,7 @@ gsu()
        shift
        if [[ "$(type -t "com_$arg")" == 'function' ]]; then
                "com_$arg" "$@"
-               if (("$ret" < 0)); then
+               if ((ret < 0)); then
                        gsu_err_msg
                        exit 1
                fi
@@ -877,6 +902,6 @@ gsu()
        ret=-$E_GSU_BAD_COMMAND
        result="$arg"
        gsu_err_msg
-       _gsu_print_available_commands
+       _gsu_print_available_commands 1>&2
        exit 1
 }