X-Git-Url: http://git.tuebingen.mpg.de/?p=gsu.git;a=blobdiff_plain;f=misc%2Fgsu%2Fsubcommand;h=3a1bc6db6697c7a0676a45ae940b930d18ee8901;hp=f09c66de613707c42794947e8b0b787c79f11f5e;hb=844b7a202a39530d00aa191bae9e3d4189b89049;hpb=c75c58e9aec5a02ffe5245df373d3eeb432769b2 diff --git a/misc/gsu/subcommand b/misc/gsu/subcommand index f09c66d..3a1bc6d 100644 --- a/misc/gsu/subcommand +++ b/misc/gsu/subcommand @@ -11,21 +11,38 @@ _gsu_usage() 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\n" - sed -ne "s/$gsu_command_regex/\1/g;T;p" $0 - } | sort | tr '\n' ' ')" + printf "help\nman\nprefs\ncomplete\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" @@ -43,6 +60,26 @@ _gsu_print_available_commands() ) 2>&1 } +gsu_complete_options() +{ + local opts="$1" cword="$2" cur opt + local -a words + + shift 2 + words=("$@") + cur="${words[$cword]}" + 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 +} + export gsu_prefs_txt=" Print the current preferences. @@ -55,7 +92,13 @@ _com_prefs() { 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" @@ -72,7 +115,7 @@ _com_prefs() 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" @@ -93,6 +136,11 @@ _com_prefs() done } +complete_prefs() +{ + gsu_complete_options "e" "$@" +} + export gsu_man_txt=" Print the manual. @@ -138,13 +186,7 @@ _com_man() _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=" @@ -155,31 +197,57 @@ Usage: help [command] Without arguments, print the list of available commands. Otherwise, print the help text for the given command." +export gsu_complete_txt=" +Command line completion. + +Usage: complete [ ...] + +When executed without argument the command writes bash code to +stdout. This code is suitable to be evaled from .bashrc to enable +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() { - 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_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 "--" - grep -A 2 "$gsu_command_regex" $0 + printf "com_complete()\n$gsu_complete_txt" | head -n 4; echo "--" + 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 for info on ." ret=$GSU_SUCCESS @@ -200,31 +268,60 @@ _com_help() ret=$GSU_SUCCESS return fi + if test "$1" = "complete"; then + echo "$gsu_complete_txt" + 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() +{ + _gsu_available_commands + 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() { @@ -255,21 +352,21 @@ 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} @@ -296,6 +393,118 @@ gsu_getopts() ret=$GSU_SUCCESS } +_com_complete() +{ + local cmd n cword + local -a words + + if (($# == 0)); then + cat <