X-Git-Url: http://git.tuebingen.mpg.de/?p=gsu.git;a=blobdiff_plain;f=subcommand;h=1b204e0dc3abe0f31ab8de41b6f148acfb6ec0cf;hp=bab8ded4ea1d9f0b99562eca7dc8d257244062a0;hb=refs%2Fheads%2Fpu;hpb=8d89aa241a5199b0a25edcf0e56da08c64aa4014 diff --git a/subcommand b/subcommand index bab8ded..d37481c 100644 --- a/subcommand +++ b/subcommand @@ -1,15 +1,16 @@ #!/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_setup)" != "function" ]]; then gsu_dir=${gsu_dir:-${BASH_SOURCE[0]%/*}} - . $gsu_dir/common || exit 1 + . "$gsu_dir/common" || exit 1 _gsu_setup fi _gsu_usage() { - gsu_short_msg "# Usage: $_gsu_self command [options]" + gsu_short_msg "# Usage: $gsu_name command [options]" } # Return an extended regular expression to match against $0. @@ -24,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)\(\)" } @@ -45,30 +46,155 @@ _gsu_available_commands() # otherwise delete it d - ' $0 + ' "$0" } | sort | tr '\n' ' ')" } -_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 +# 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 i + local c c1 c2 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 - printf "\t" - if test ${#i} -lt 8; then - printf "\t" - fi + result+="false" fi done - echo -) 2>&1 + 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 cmds + 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 + 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 + ((count % cols == 0)) && printf '\n' + printf '%-*s' $maxlen $cmd + let ++count + 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 @@ -80,42 +206,46 @@ 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 } -export gsu_prefs_txt=" +declare -A _gsu_help_text=() # indexed by autocmd +com_prefs_options='e' + +_gsu_help_text['prefs']=' 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 + ((ret < 0)) && return gsu_check_arg_count $# 0 0 - (($ret < 0)) && return + ((ret < 0)) && return if [[ "$o_e" == "true" ]]; then ret=-$E_GSU_MKDIR result="${conf%/*}" mkdir -p "$result" - [[ $? -ne 0 ]] && return + (($? != 0)) && return ret=-$E_GSU_EDITOR result="${EDITOR:-vi}" - "$result" "$conf" - [[ $? -ne 0 ]] && return + "$result" $conf + (($? != 0)) && return ret=$GSU_SUCCESS return fi @@ -133,43 +263,334 @@ _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" "$@" +} + +_gsu_man_options='m:b:' + +complete_man() +{ + gsu_complete_options "$_gsu_man_options" "$@" + ((ret > 0)) && return + gsu_cword_is_option_parameter "$_gsu_man_options" "$@" + [[ "$result" == 'm' ]] && printf 'roff\ntext\nhtml\n' } -export gsu_man_txt=" +_gsu_help_text['man']=' Print the manual. -Usage: man" +Usage: man [-m ] [-b ] + +-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 +dumps the man page to stdout and exits. + +Otherwise, it tries to display the manual page as follows. In text +mode, plain text is piped to $PAGER. In roff mode, the roff output +is filtered through nroff, then piped to $PAGER. For both formats, +if $PAGER is unset, less(1) is assumed. + +In html mode, html output is written to a temporary file, and this +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. +' + +_gsu_read_line() +{ + local -n p="$1" + local l OIFS="$IFS" + + IFS= + read -r l || return + IFS="$OIFS" + p="$l" +} + +_gsu_change_roffify_state() +{ + local -n statep="$1" + local new_state="$2" + local old_state="$statep" + + [[ "$old_state" == "$new_state" ]] && return 0 + + case "$old_state" in + text);; + example) printf '.EE\n';; + enum) printf '.RE\n';; + esac + case "$new_state" in + text);; + example) printf '.EX\n';; + enum) printf '.RS 2\n';; + esac + + statep="$new_state" + return 1 +} + +_gsu_print_protected_roff_line() +{ + local line="$1" + local -i n=0 + + while [[ "${line:$n:1}" == ' ' ]]; do + let n++ + done + line="${line:$n}" + printf '\\&%s\n' "${line//\\/\\\\}" +} + +_gsu_roffify_maindoc() +{ + local state='text' TAB=' ' + local line next_line + local -i n + + _gsu_read_line 'line' || return + while _gsu_read_line next_line; do + if [[ "$next_line" =~ ^(----|====|~~~~) ]]; then # heading + printf '.SS %s\n' "$line" + _gsu_read_line line || return + _gsu_change_roffify_state 'state' 'text' + continue + fi + if [[ "${line:0:1}" == "$TAB" ]]; then # example + _gsu_change_roffify_state 'state' 'example' + _gsu_print_protected_roff_line "$line" + line="$next_line" + continue + fi + n=0 + while [[ "${line:$n:1}" == ' ' ]]; do + let n++ + done + line=${line:$n}; + if [[ "${line:0:1}" == '*' ]]; then # enum + line=${line#\*} + _gsu_change_roffify_state 'state' 'enum' + printf '\n\(bu %s\n' "$line" + line="$next_line" + continue + fi + if [[ "$line" =~ ^$ ]]; then # new paragraph + _gsu_change_roffify_state 'state' 'text' + printf '.PP\n' + else + _gsu_print_protected_roff_line "$line" + fi + line="$next_line" + done + _gsu_print_protected_roff_line "$line" +} + +_gsu_extract_maindoc() +{ + sed -e '1,/^#\{70,\}/d' -e '/^#\{70,\}/,$d' -e 's/^# *//' -e 's/^#//g' "$0" +} + +_gsu_roffify_cmds() +{ + local line cmd= desc= state='text' TAB=' ' + + while _gsu_read_line line; do + if [[ "${line:0:1}" != '#' ]]; then # com_foo() + line="${line#com_}" + cmd="${line%()}" + continue + fi + line="${line####}" + if [[ "$line" =~ ^[[:space:]]*$ ]]; then + printf '.PP\n' + _gsu_change_roffify_state 'state' 'text' + continue + fi + if [[ -n "$cmd" ]]; then # desc or usage + if [[ -z "$desc" ]]; then # desc + desc="$line" + continue + fi + # usage + _gsu_change_roffify_state 'state' 'text' + printf '\n.SS %s \\- %s\n' "$cmd" "$desc" + printf '\n.I %s\n' "$line" + cmd= + desc= + continue + fi + line="${line# }" + if [[ "${line:0:1}" == "$TAB" ]]; then + _gsu_change_roffify_state 'state' 'example' + _gsu_print_protected_roff_line "$line" + continue + fi + if [[ "$line" == -*:* ]]; then + _gsu_change_roffify_state 'state' 'enum' + printf '.PP\n.B %s:\n' "${line%%:*}" + _gsu_print_protected_roff_line "${line#*:}" + continue + fi + _gsu_print_protected_roff_line "$line" + done +} + +_gsu_roffify_autocmds() +{ + local cmd help_txt + + 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() +{ + local name="$1" sect_num="$2" month_year="$3" pkg="$4" sect_name="$5" + local purpose="$6" + local ere + + cat << EOF +.TH "${name^^}" "$sect_num" "$month_year" "$pkg" "$sect_name" +.SH NAME +$name \- $purpose +.SH SYNOPSIS +.B $name +\fI\,\/\fR [\fI\,\/\fR] [\fI\,\/\fR] +.SH DESCRIPTION +EOF + _gsu_extract_maindoc | _gsu_roffify_maindoc + + printf '\n.SH "GENERIC SUBCOMMANDS"\n' + printf 'The following commands are automatically created by gsu\n' + _gsu_roffify_autocmds + + printf '\n.SH "LIST OF SUBCOMMANDS"\n' + printf 'Each command has its own set of options as described below.\n' + + _gsu_get_command_regex + ere="$result" + # only consider lines in the comment of the function + sed -nEe '/'"$ere"'/,/^[^#]/p' "$0" | _gsu_roffify_cmds +} + +_gsu_file_mtime() +{ + local file="$1" + result="$(find "$file" -printf '%TB %TY' 2>/dev/null)" # GNU + (($? == 0)) && [[ -n "$result" ]] && return + result="$(stat -f %Sm -t '%B %Y' "$file" 2>/dev/null)" # BSD + (($? == 0)) && [[ -n "$result" ]] && return + result="$(date '+%B %Y' 2>/dev/null)" # POSIX + (($? == 0)) && [[ -n "$result" ]] && return + result='[unknown date]' +} -_com_man() +com_man() { local equal_signs="==================================================" local minus_signs="--------------------------------------------------" - local com num + local filter='cat' pager='cat' browser=${BROWSER:-elinks} tmpfile= + local com num isatty pipeline - echo "$_gsu_self (_${gsu_banner_txt}_) manual" - echo "${equal_signs:0:${#_gsu_self} + ${#gsu_banner_txt} + 16}" - echo + gsu_getopts "$_gsu_man_options" + eval "$result" + ((ret < 0)) && return + if [[ -n "$o_b" ]]; then + o_m='html' + browser="$o_b" + elif [[ -z "$o_m" ]]; then + o_m='roff' + fi - sed -e '1,/^#\{70,\}/d' -e '/^#\{70,\}/,$d' $0 -e 's/^# *//' + _gsu_isatty && isatty='true' || isatty='false' + if [[ "$o_m" == 'roff' ]]; then + if [[ "$isatty" == 'true' ]]; then + filter='nroff -Tutf8 -mandoc' + pager="${PAGER:-less}" + fi + elif [[ "$o_m" == 'text' ]]; then + if [[ "$isatty" == 'true' ]]; then + pager="${PAGER:-less}" + fi + elif [[ "$o_m" == 'html' ]]; then + filter='groff -T html -m man' + if [[ "$isatty" == 'true' ]]; then + gsu_make_tempfile "gsu_html_man.XXXXXX.html" + ((ret < 0)) && return || tmpfile="$result" + trap "rm -f $tmpfile" RETURN EXIT + fi + fi + [[ "$pager" == 'less' ]] && export LESS=${LESS-RI} + case "$o_m" in + roff|html) + _gsu_file_mtime "$0" + _gsu_roff_man "$gsu_name" '1' "$result" \ + "${gsu_package-${gsu_name^^}(1)}" \ + "User Commands" "${gsu_banner_txt}" \ + | $filter | { + if [[ -n "$tmpfile" ]]; then + cat > "$tmpfile" + else + $pager + fi + } + if (($? != 0)); then + ret=-$E_GSU_XCMD + result="filter: $filter" + return + fi + if [[ -n "$tmpfile" ]]; then + ret=-$E_GSU_XCMD + result="$browser" + "$browser" "$tmpfile" || return + fi + ret=$GSU_SUCCESS + return + ;; + text) ;; + "") ;; + *) + ret=-$E_GSU_INVAL + result="$o_m" + return + esac + { + echo "$gsu_name (_${gsu_banner_txt}_) manual" + echo "${equal_signs:0:${#gsu_name} + ${#gsu_banner_txt} + 16}" + echo + _gsu_extract_maindoc echo "----" - echo - echo "$_gsu_self usage" - echo "${minus_signs:0:${#_gsu_self} + 6}" + echo + echo "$gsu_name usage" + echo "${minus_signs:0:${#gsu_name} + 6}" printf "\t" _gsu_usage 2>&1 echo "Each command has its own set of options as described below." @@ -179,34 +600,31 @@ _com_man() echo "Available commands:" _gsu_available_commands - for com in $result; do + for com in $result; do num=${#com} - if test $num -lt 4; then - num=4 - fi - echo "${minus_signs:0:$num}" - echo "$com" - echo "${minus_signs:0:$num}" - $0 help $com + ((num < 4)) && num=4 + echo "${minus_signs:0:$num}" + echo "$com" + echo "${minus_signs:0:$num}" + "$0" help "$com" echo - done - ret=$GSU_SUCCESS -} - -_gsu_banner_msg() -{ - gsu_short_msg "### $_gsu_self -- ###" + done + } | $pager + ret=$GSU_SUCCESS } -export 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.' -export gsu_complete_txt=" +_gsu_help_text['complete']=' Command line completion. Usage: complete [ ...] @@ -218,24 +636,34 @@ 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() +com_help_options='a' +com_help() { - local a b ere tab=' ' + local ere tab=' ' txt + + gsu_getopts "$com_help_options" + eval "$result" + ((ret < 0)) && return _gsu_get_command_regex ere="$result" if (($# == 0)); then - _gsu_banner_msg 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 + 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 # remove everything but the command name @@ -256,36 +684,36 @@ _com_help() y/\n/'"$tab"'/ # and print the sucker - p' - echo - echo "# Try $_gsu_self help for info on ." - 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" - ret=$GSU_SUCCESS - return - fi - if test "$1" = "prefs"; then - echo "$gsu_prefs_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 for info on , 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" = "complete"; then - echo "$gsu_complete_txt" + for cmd in "${!_gsu_help_text[@]}"; do + [[ "$1" != "$cmd" ]] && continue + printf '%s\n' "${_gsu_help_text["$cmd"]}" ret=$GSU_SUCCESS return - fi - ret=$GSU_SUCCESS + done _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 @@ -308,7 +736,8 @@ _com_help() :p p } - ' $0 + ' "$0" + ret=$GSU_SUCCESS } complete_help() @@ -317,92 +746,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 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 -lt 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 @@ -413,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 @@ -421,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 @@ -431,9 +780,10 @@ 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 - complete_$cmd "$cword" "${words[@]}" + # 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 } @@ -449,24 +799,25 @@ EOF # 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= - (($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 + [[ ! "$prev" =~ ^-.*$opt$ ]] && continue result="$opt" return done @@ -495,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 @@ -511,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. @@ -532,70 +883,25 @@ gsu_get_unnamed_arg_num() gsu() { local i - _gsu_available_commands - gsu_cmds="$result" - if test $# -eq 0; then + + if (($# == 0)); then _gsu_usage _gsu_print_available_commands exit 1 fi arg="$1" shift - # check internal commands - if [[ "$arg" = "help" || "$arg" = "man" || "$arg" = "prefs" || "$arg" = "complete" ]]; then - _com_$arg "$@" - if [[ "$ret" -lt 0 ]]; then + 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" -lt 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 + _gsu_print_available_commands 1>&2 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 reqired -# -gsu_check_arg_count() -{ - ret=-$E_GSU_BAD_ARG_COUNT - if [[ $# -eq 2 ]]; then # only num1 is given - result="at least $2 args required, $1 given" - [[ $1 -lt $2 ]] && return - ret=$GSU_SUCCESS - return - fi - # num1 and num2 given - result="need at least $2 args, $1 given" - [[ $1 -lt $2 ]] && return - result="need at most $3 args, $1 given" - [[ $1 -gt $3 ]] && return - ret=$GSU_SUCCESS -}