X-Git-Url: http://git.tuebingen.mpg.de/?p=gsu.git;a=blobdiff_plain;f=subcommand;h=c04f23feeb5ae018ce0e0ae1fea46efe20318b40;hp=3ff31f0a6492e6d3420d533de96d9e02e358825a;hb=4167cc8db6c44314335cffd9c1a80f6da7ae4a69;hpb=ee0253579a48cddcf022a7978afe68bc57311313 diff --git a/subcommand b/subcommand index 3ff31f0..c04f23f 100644 --- a/subcommand +++ b/subcommand @@ -4,7 +4,7 @@ if [[ "$(type -t _gsu_setup)" != "function" ]]; then gsu_dir=${gsu_dir:-${BASH_SOURCE[0]%/*}} - . $gsu_dir/common || exit 1 + . "$gsu_dir/common" || exit 1 _gsu_setup fi @@ -25,7 +25,7 @@ _gsu_usage() # 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)\(\)" } @@ -46,17 +46,133 @@ _gsu_available_commands() # otherwise delete it d - ' $0 + ' "$0" } | sort | tr '\n' ' ')" } +# Check number of arguments. +# +# Usage: gsu_check_arg_count [] +# +# Check that is between and inclusively. +# If only 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() { - local cmd + local cmd cmds local -i count=0 + _gsu_available_commands + cmds="$result" printf 'Available commands:\n' - for cmd in $gsu_cmds; do + for cmd in $cmds; do printf '%s' "$cmd" let ++count if (($count % 4)); then @@ -91,19 +207,23 @@ gsu_complete_options() done } -export gsu_prefs_txt=" +com_prefs_options='e' + +_gsu_prefs_txt=" 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}" - gsu_getopts "e" + gsu_getopts "$com_prefs_options" eval "$result" (($ret < 0)) && return gsu_check_arg_count $# 0 0 @@ -135,41 +255,54 @@ _com_prefs() printf "# optional" ;; esac - printf " $option_type: $description" + printf "%s: %s" "$option_type" "$description" 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" - printf "$name=$val" + printf "%s=%s" "$name" "$val" [[ "$val" == "$default_value" ]] && printf " # default" echo done } +_gsu_isatty() +{( + exec 3<&1 + stty 0<&3 &> /dev/null +)} + complete_prefs() { - gsu_complete_options "e" "$@" + gsu_complete_options "$com_prefs_options" "$@" } -export gsu_man_txt=" +_gsu_man_txt=' Print the manual. -Usage: man" +Usage: man -_com_man() +If stdout associated with a terminal device, output is piped to +$PAGER. If $PAGER is unset, less(1) is assumed. +' + +com_man() { 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 - 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 "$gsu_name usage" echo "${minus_signs:0:${#gsu_name} + 6}" printf "\t" @@ -181,19 +314,20 @@ _com_man() echo "Available commands:" _gsu_available_commands - for com in $result; do + for com in $result; do 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 - done - ret=$GSU_SUCCESS + done + } | $pager + ret=$GSU_SUCCESS } -export gsu_help_txt=" +_gsu_help_txt=" Print online help. Usage: help [command] @@ -201,7 +335,7 @@ Usage: help [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 [ ...] @@ -215,9 +349,9 @@ written to stdout. This can be used from the completion function of the subcommand. " -_com_help() +com_help() { - local a b ere tab=' ' + local ere tab=' ' _gsu_get_command_regex ere="$result" @@ -226,11 +360,11 @@ _com_help() 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 @@ -258,29 +392,29 @@ _com_help() 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 - echo "$gsu_man_txt" + echo "$_gsu_man_txt" 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 - echo "$gsu_complete_txt" + echo "$_gsu_complete_txt" 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 @@ -303,7 +437,7 @@ _com_help() :p p } - ' $0 + ' "$0" } complete_help() @@ -312,92 +446,7 @@ complete_help() 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 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 -} - -_com_complete() +com_complete() { local cmd n cword local -a words @@ -427,8 +476,8 @@ EOF 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 } @@ -461,7 +510,7 @@ gsu_cword_is_option_parameter() opt="${opts:$i:1}" [[ "${opts:$(($i + 1)):1}" != ":" ]] && continue let i++ - [[ "$prev" != "-$opt" ]] && continue + [[ ! "$prev" =~ ^-.*$opt$ ]] && continue result="$opt" return done @@ -527,8 +576,7 @@ gsu_get_unnamed_arg_num() gsu() { local i - _gsu_available_commands - gsu_cmds="$result" + if (($# == 0)); then _gsu_usage _gsu_print_available_commands @@ -536,60 +584,17 @@ gsu() 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 - - # 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 } - -# Check number of arguments. -# -# Usage: gsu_check_arg_count [] -# -# Check that is between and inclusively. -# If only 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 -}