]> git.tuebingen.mpg.de Git - gsu.git/blobdiff - subcommand
subcommand: Declare $opt as local.
[gsu.git] / subcommand
index 05860f68395f67ffd3e9a51a60e48eb857a9cca8..7318c9e880396e5118a117639050fac2ba063e95 100644 (file)
@@ -1,9 +1,11 @@
 #!/bin/bash
 #!/bin/bash
-# (C) 2006-2011 Andre Noll
+# Copyright (C) 2006 Andre Noll
+# Licensed under the LGPL, version 3. See COPYING and COPYING.LESSER.
 
 
-if [[ $(type -t gsu_is_a_number) != "function" ]]; then
-       GSU_DIR=${GSU_DIR:=${HOME-}/.gsu}
-       . $GSU_DIR/common || exit 1
+if [[ "$(type -t _gsu_setup)" != "function" ]]; then
+       gsu_dir=${gsu_dir:-${BASH_SOURCE[0]%/*}}
+       . "$gsu_dir/common" || exit 1
+       _gsu_setup
 fi
 
 _gsu_usage()
 fi
 
 _gsu_usage()
@@ -23,7 +25,7 @@ _gsu_usage()
 # exactly one parenthesized subexpression for matching the command name.
 _gsu_get_command_regex()
 {
 # exactly one parenthesized subexpression for matching the command name.
 _gsu_get_command_regex()
 {
-       local cmd="${1:-[-a-zA-Z_0-9]+}"
+       local cmd="${1:-[-a-zA-Z_0-9]{1,\}}"
        result="^com_($cmd)\(\)"
 }
 
        result="^com_($cmd)\(\)"
 }
 
@@ -44,30 +46,147 @@ _gsu_available_commands()
 
                        # otherwise delete it
                        d
 
                        # otherwise delete it
                        d
-               ' $0
+               ' "$0"
        } | sort | tr '\n' ' ')"
 }
 
        } | sort | tr '\n' ' ')"
 }
 
+# Check number of arguments.
+#
+# Usage: gsu_check_arg_count <num_given> <num1> [<num2>]
+#
+# Check that <num_given> is between <num1> and <num2> inclusively.
+# If only <num1> ist given, num2 is assumed to be infinity.
+#
+# Examples:
+#      0 0 no argument allowed
+#      1 1 exactly one argument required
+#      0 2 at most two arguments admissible
+#      2   at least two arguments required
+gsu_check_arg_count()
+{
+       ret=-$E_GSU_BAD_ARG_COUNT
+       if (($# == 2)); then # only num1 is given
+               result="at least $2 args required, $1 given"
+               (($1 < $2)) && return
+               ret=$GSU_SUCCESS
+               return
+       fi
+       # num1 and num2 given
+       result="need at least $2 args, $1 given"
+       (($1 < $2)) && return
+       result="need at most $3 args, $1 given"
+       (($1 > $3)) && return
+       ret=$GSU_SUCCESS
+}
+
+# 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 argument and either the empty
+# string or the given argument for options that take an argument.
+#
+# Example:
+#      gsu_getopts abc:x:y
+#      eval "$result"
+#      (($ret < 0)) && return
+#
+#      [[ "$o_a" = 'true' ]] && echo 'The -a flag was given'
+#      [[ -n "$o_c" ]] && echo "The -c option was given with arg $o_c"
+gsu_getopts()
+{
+       local i c tab=' ' cr='
+'
+
+       gsu_check_arg_count $# 1 1
+       if (($ret < 0)); then
+               gsu_err_msg
+               exit 1
+       fi
+
+       ret=-$E_GSU_GETOPTS
+       result="invalid optstring $1"
+       if [[ -z "$1" ]] || grep -q '::' <<< "$1" ; then
+               gsu_err_msg
+               exit 1
+       fi
+
+       for ((i=0; i < ${#1}; i++)); do
+               c=${1:$i:1}
+               case "$c" in
+               [a-zA-Z:]);;
+               *)
+                       ret=-$E_GSU_GETOPTS
+                       result="invalid character $c in optstring"
+                       gsu_err_msg
+                       exit 1
+               esac
+       done
+       result="local _gsu_getopts_opt"
+       for ((i=0; i < ${#1}; i++)); do
+               c1=${1:$i:1}
+               c2=${1:$(($i + 1)):1}
+               result+=" o_$c1="
+               if [[ "$c2" = ":" ]]; then
+                       let i++
+               else
+                       result+="false"
+               fi
+       done
+       result+="
+       OPTIND=1
+       while getopts $1 _gsu_getopts_opt \"\$@\"; do
+               case \"\$_gsu_getopts_opt\" in
+"
+       for ((i=0; i < ${#1}; i++)); do
+               c1=${1:$i:1}
+               c2=${1:$(($i + 1)):1}
+               result+="$tab$tab$c1) o_$c1="
+               if [[ "$c2" = ":" ]]; then
+                       result+="\"\$OPTARG\""
+                       let i++
+               else
+                       result+="true"
+               fi
+               result+=";;$cr"
+       done
+       result+="
+               *)
+                       ret=-\$E_GSU_GETOPTS
+                       result=\"invalid option given\"
+                       return
+                       ;;
+               esac
+       done
+       shift \$((\$OPTIND - 1))
+"
+       ret=$GSU_SUCCESS
+}
+
 _gsu_print_available_commands()
 _gsu_print_available_commands()
-{(
-       local i count=0
-       gsu_short_msg "Available commands:"
-       for i in $gsu_cmds; do
-               printf "$i"
-               count=$(($count + 1))
-               if test $(($count % 4)) -eq 0; then
-                       echo
+{
+       local cmd cmds
+       local -i count=0
+
+       _gsu_available_commands
+       cmds="$result"
+       printf 'Available commands:\n'
+       for cmd in $cmds; do
+               printf '%s' "$cmd"
+               let ++count
+               if (($count % 4)); then
+                       printf '\t'
+                       ((${#cmd} < 8)) && printf '\t'
                else
                else
-                       printf "\t"
-                       if test ${#i} -lt 8; then
-                               printf "\t"
-                       fi
+                       printf '\n'
                fi
        done
                fi
        done
-       echo
-) 2>&1
+       printf '\n'
 }
 
 }
 
+# Print all options of the given optstring to stdout if the word in the current
+# command line begins with a hyphen character.
 gsu_complete_options()
 {
        local opts="$1" cword="$2" cur opt
 gsu_complete_options()
 {
        local opts="$1" cword="$2" cur opt
@@ -88,19 +207,23 @@ gsu_complete_options()
        done
 }
 
        done
 }
 
-export gsu_prefs_txt="
+com_prefs_options='e'
+
+_gsu_prefs_txt="
 Print the current preferences.
 
 Usage: prefs [-e]
 
 Print the current preferences.
 
 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 cmt config variables, together
-with their current value and the default value."
-_com_prefs()
+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()
 {
        local i conf="${gsu_config_file:=${HOME:-}/.$gsu_name.rc}"
 
 {
        local i conf="${gsu_config_file:=${HOME:-}/.$gsu_name.rc}"
 
-       gsu_getopts "e"
+       gsu_getopts "$com_prefs_options"
        eval "$result"
        (($ret < 0)) && return
        gsu_check_arg_count $# 0 0
        eval "$result"
        (($ret < 0)) && return
        gsu_check_arg_count $# 0 0
@@ -132,41 +255,54 @@ _com_prefs()
                        printf "# optional"
                        ;;
                esac
                        printf "# optional"
                        ;;
                esac
-               printf " $option_type: $description"
+               printf "%s: %s" "$option_type" "$description"
                if [[ "$required" != "yes" && "$required" != "true" ]]; then
                if [[ "$required" != "yes" && "$required" != "true" ]]; then
-                       printf " [$default_value]"
+                       printf " [%s]" "$default_value"
                fi
                echo
                [[ -n "$help_text" ]] && sed -e '/^[    ]*$/d; s/^[     ]*/#    /g' <<< "$help_text"
                fi
                echo
                [[ -n "$help_text" ]] && sed -e '/^[    ]*$/d; s/^[     ]*/#    /g' <<< "$help_text"
-               printf "$name=$val"
+               printf "%s=%s" "$name" "$val"
                [[ "$val" == "$default_value" ]] && printf " # default"
                echo
        done
 }
 
                [[ "$val" == "$default_value" ]] && printf " # default"
                echo
        done
 }
 
+_gsu_isatty()
+{(
+       exec 3<&1
+       stty 0<&3 &> /dev/null
+)}
+
 complete_prefs()
 {
 complete_prefs()
 {
-       gsu_complete_options "e" "$@"
+       gsu_complete_options "$com_prefs_options" "$@"
 }
 
 }
 
-export gsu_man_txt="
+_gsu_man_txt='
 Print the manual.
 
 Print the manual.
 
-Usage: man"
+Usage: man
+
+If stdout associated with a terminal device, output is piped to
+$PAGER. If $PAGER is unset, less(1) is assumed.
+'
 
 
-_com_man()
+com_man()
 {
        local equal_signs="=================================================="
        local minus_signs="--------------------------------------------------"
 {
        local equal_signs="=================================================="
        local minus_signs="--------------------------------------------------"
-        local com num
+       local com num pager='cat'
 
 
+       _gsu_isatty && pager="${PAGER:-less}"
+       [[ "$pager" == 'less' ]] && export LESS=${LESS-RI}
+       {
        echo "$gsu_name (_${gsu_banner_txt}_) manual"
        echo "${equal_signs:0:${#gsu_name} + ${#gsu_banner_txt} + 16}"
        echo
 
        echo "$gsu_name (_${gsu_banner_txt}_) manual"
        echo "${equal_signs:0:${#gsu_name} + ${#gsu_banner_txt} + 16}"
        echo
 
-        sed -e '1,/^#\{70,\}/d' -e '/^#\{70,\}/,$d' $0 -e 's/^# *//'
+       sed -e '1,/^#\{70,\}/d' -e '/^#\{70,\}/,$d' "$0" -e 's/^# *//'
        echo "----"
        echo "----"
-        echo
+       echo
        echo "$gsu_name usage"
        echo "${minus_signs:0:${#gsu_name} + 6}"
        printf "\t"
        echo "$gsu_name usage"
        echo "${minus_signs:0:${#gsu_name} + 6}"
        printf "\t"
@@ -178,19 +314,20 @@ _com_man()
        echo "Available commands:"
 
        _gsu_available_commands
        echo "Available commands:"
 
        _gsu_available_commands
-        for com in $result; do
+       for com in $result; do
                num=${#com}
                (($num < 4)) && num=4
                num=${#com}
                (($num < 4)) && num=4
-                echo "${minus_signs:0:$num}"
-                echo "$com"
-                echo "${minus_signs:0:$num}"
-                $0 help $com
+               echo "${minus_signs:0:$num}"
+               echo "$com"
+               echo "${minus_signs:0:$num}"
+               "$0" help "$com"
                echo
                echo
-        done
-        ret=$GSU_SUCCESS
+       done
+       } | $pager
+       ret=$GSU_SUCCESS
 }
 
 }
 
-export gsu_help_txt="
+_gsu_help_txt="
 Print online help.
 
 Usage: help [command]
 Print online help.
 
 Usage: help [command]
@@ -198,7 +335,7 @@ Usage: help [command]
 Without arguments, print the list of available commands. Otherwise,
 print the help text for the given command."
 
 Without arguments, print the list of available commands. Otherwise,
 print the help text for the given command."
 
-export gsu_complete_txt="
+_gsu_complete_txt="
 Command line completion.
 
 Usage: complete [<cword> <word>...]
 Command line completion.
 
 Usage: complete [<cword> <word>...]
@@ -212,9 +349,9 @@ written to stdout. This can be used from the completion function of
 the subcommand.
 "
 
 the subcommand.
 "
 
-_com_help()
+com_help()
 {
 {
-       local a b ere tab='     '
+       local ere tab=' '
 
        _gsu_get_command_regex
        ere="$result"
 
        _gsu_get_command_regex
        ere="$result"
@@ -223,11 +360,11 @@ _com_help()
                gsu_short_msg "### $gsu_name -- $gsu_banner_txt ###"
                _gsu_usage 2>&1
                {
                gsu_short_msg "### $gsu_name -- $gsu_banner_txt ###"
                _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 "--"
-                       printf "com_complete()\n$gsu_complete_txt" | head -n 4; echo "--"
-                       grep -EA 2 "$ere" $0
+                       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 "--"
+                       grep -EA 2 "$ere" "$0"
                } | grep -v -- '--' \
                        | sed -En "/$ere/"'!d
                                # remove everything but the command name
                } | grep -v -- '--' \
                        | sed -En "/$ere/"'!d
                                # remove everything but the command name
@@ -255,29 +392,29 @@ _com_help()
                return
        fi
        if test "$1" = "help"; then
                return
        fi
        if test "$1" = "help"; then
-               echo "$gsu_help_txt"
+               echo "$_gsu_help_txt"
                ret=$GSU_SUCCESS
                return
        fi
        if test "$1" = "man"; then
                ret=$GSU_SUCCESS
                return
        fi
        if test "$1" = "man"; then
-               echo "$gsu_man_txt"
+               echo "$_gsu_man_txt"
                ret=$GSU_SUCCESS
                return
        fi
        if test "$1" = "prefs"; then
                ret=$GSU_SUCCESS
                return
        fi
        if test "$1" = "prefs"; then
-               echo "$gsu_prefs_txt"
+               echo "$_gsu_prefs_txt"
                ret=$GSU_SUCCESS
                return
        fi
        if test "$1" = "complete"; then
                ret=$GSU_SUCCESS
                return
        fi
        if test "$1" = "complete"; then
-               echo "$gsu_complete_txt"
+               echo "$_gsu_complete_txt"
                ret=$GSU_SUCCESS
                return
        fi
        ret=$GSU_SUCCESS
        _gsu_get_command_regex "$1"
        ere="$result"
                ret=$GSU_SUCCESS
                return
        fi
        ret=$GSU_SUCCESS
        _gsu_get_command_regex "$1"
        ere="$result"
-       if ! grep -Eq "$ere" $0; then
+       if ! grep -Eq "$ere" "$0"; then
                _gsu_print_available_commands
                result="$1"
                ret=-$E_GSU_BAD_COMMAND
                _gsu_print_available_commands
                result="$1"
                ret=-$E_GSU_BAD_COMMAND
@@ -300,7 +437,7 @@ _com_help()
                        :p
                        p
                }
                        :p
                        p
                }
-       ' $0
+       ' "$0"
 }
 
 complete_help()
 }
 
 complete_help()
@@ -309,92 +446,7 @@ complete_help()
        echo "$result"
 }
 
        echo "$result"
 }
 
-# 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 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 < 0)) && return
-#
-#      [[ "$o_a" = 'true' ]] && echo 'The -a flag was given'
-#      [[ -n "$o_c" ]] && echo "The -c option was given with arg $o_c"
-gsu_getopts()
-{
-       local i c tab=' ' cr='
-'
-
-       gsu_check_arg_count $# 1 1
-       if (($ret < 0)); then
-               gsu_err_msg
-               exit 1
-       fi
-
-       ret=-$E_GSU_GETOPTS
-       result="invalid optstring $1"
-       if [[ -z "$1" ]] || grep -q '::' <<< "$1" ; then
-               gsu_err_msg
-               exit 1
-       fi
-
-       for ((i=0; i < ${#1}; i++)); do
-               c=${1:$i:1}
-               case "$c" in
-               [a-zA-Z:]);;
-               *)
-                       ret=-$E_GSU_GETOPTS
-                       result="invalid character $c in optstring"
-                       gsu_err_msg
-                       exit 1
-               esac
-       done
-       result="local _gsu_getopts_opt"
-       for ((i=0; i < ${#1}; i++)); do
-               c1=${1:$i:1}
-               c2=${1:$(($i + 1)):1}
-               result+=" o_$c1="
-               if [[ "$c2" = ":" ]]; then
-                       let i++
-               else
-                       result+="false"
-               fi
-       done
-       result+="
-       OPTIND=1
-       while getopts $1 _gsu_getopts_opt \"\$@\"; do
-               case \"\$_gsu_getopts_opt\" in
-"
-       for ((i=0; i < ${#1}; i++)); do
-               c1=${1:$i:1}
-               c2=${1:$(($i + 1)):1}
-               result+="$tab$tab$c1) o_$c1="
-               if [[ "$c2" = ":" ]]; then
-                       result+="\"\$OPTARG\""
-                       let i++
-               else
-                       result+="true"
-               fi
-               result+=";;$cr"
-       done
-       result+="
-               *)
-                       ret=-\$E_GSU_GETOPTS
-                       result=\"invalid option given\"
-                       return
-                       ;;
-               esac
-       done
-       shift \$((\$OPTIND - 1))
-"
-       ret=$GSU_SUCCESS
-}
-
-_com_complete()
+com_complete()
 {
        local cmd n cword
        local -a words
 {
        local cmd n cword
        local -a words
@@ -424,8 +476,8 @@ EOF
        words=("$@")
        cmd="${words[1]}"
        ret=$GSU_SUCCESS # It's not an error if no completer was defined
        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[@]}"
+       [[ "$(type -t "complete_$cmd")" != "function" ]] && return
+       "complete_$cmd" "$cword" "${words[@]}"
        # ignore errors, they would only clutter the completion output
        ret=$GSU_SUCCESS
 }
        # ignore errors, they would only clutter the completion output
        ret=$GSU_SUCCESS
 }
@@ -441,7 +493,8 @@ EOF
 #
 gsu_cword_is_option_parameter()
 {
 #
 gsu_cword_is_option_parameter()
 {
-       local opts="$1" cword="$2" prev i n
+       local opts="$1" cword="$2"
+       local opt prev i n
        local -a words
 
        result=
        local -a words
 
        result=
@@ -458,7 +511,7 @@ gsu_cword_is_option_parameter()
                opt="${opts:$i:1}"
                [[ "${opts:$(($i + 1)):1}" != ":" ]] && continue
                let i++
                opt="${opts:$i:1}"
                [[ "${opts:$(($i + 1)):1}" != ":" ]] && continue
                let i++
-               [[ "$prev" != "-$opt" ]] && continue
+               [[ ! "$prev" =~ ^-.*$opt$ ]] && continue
                result="$opt"
                return
        done
                result="$opt"
                return
        done
@@ -524,9 +577,7 @@ gsu_get_unnamed_arg_num()
 gsu()
 {
        local i
 gsu()
 {
        local i
-       _gsu_setup
-       _gsu_available_commands
-       gsu_cmds="$result"
+
        if (($# == 0)); then
                _gsu_usage
                _gsu_print_available_commands
        if (($# == 0)); then
                _gsu_usage
                _gsu_print_available_commands
@@ -534,61 +585,17 @@ gsu()
        fi
        arg="$1"
        shift
        fi
        arg="$1"
        shift
-       # check internal commands
-       if [[ "$arg" = "help" || "$arg" = "man" || "$arg" = "prefs" || "$arg" = "complete" ]]; then
-               _com_$arg "$@"
+       if [[ "$(type -t "com_$arg")" == 'function' ]]; then
+               "com_$arg" "$@"
                if (("$ret" < 0)); then
                        gsu_err_msg
                        exit 1
                fi
                exit 0
        fi
                if (("$ret" < 0)); then
                        gsu_err_msg
                        exit 1
                fi
                exit 0
        fi
-
-       # external commands
-       for i in $gsu_cmds; do
-               if test "$arg" = "$i"; then
-                       com_$arg "$@"
-                       if (("$ret" < 0)); then
-                               gsu_err_msg
-                               exit 1
-                       fi
-                       exit 0
-               fi
-       done
-
        ret=-$E_GSU_BAD_COMMAND
        result="$arg"
        gsu_err_msg
        _gsu_print_available_commands
        exit 1
 }
        ret=-$E_GSU_BAD_COMMAND
        result="$arg"
        gsu_err_msg
        _gsu_print_available_commands
        exit 1
 }
-
-# Check number of arguments.
-#
-# Usage: gsu_check_arg_count <num_given> <num1> [<num2>]
-#
-# Check that <num_given> is between <num1> and <num2> inclusively.
-# If only <num1> ist given, num2 is assumed to be infinity.
-#
-# Examples:
-#      0 0 no argument allowed
-#      1 1 exactly one argument required
-#      0 2 at most two arguments admissible
-#      2   at least two arguments reqired
-#
-gsu_check_arg_count()
-{
-       ret=-$E_GSU_BAD_ARG_COUNT
-       if (($# == 2)); then # only num1 is given
-               result="at least $2 args required, $1 given"
-               (($1 < $2)) && return
-               ret=$GSU_SUCCESS
-               return
-       fi
-       # num1 and num2 given
-       result="need at least $2 args, $1 given"
-       (($1 < $2)) && return
-       result="need at most $3 args, $1 given"
-       (($1 > $3)) && return
-       ret=$GSU_SUCCESS
-}