#!/bin/bash # (C) 2006-2011 Andre Noll if [[ $(type -t gsu_is_a_number) != "function" ]]; then GSU_DIR=${GSU_DIR:=${HOME-}/.gsu} . $GSU_DIR/common || exit 1 fi _gsu_usage() { gsu_short_msg "# Usage: $_gsu_self command [options]" } # Return an extended regular expression to match against $0. # # When called without argument, the expression matches all lines which define a # subcommand. # # If an argument is given, the returned expression matches only the subcommand # passed as $1. This is useful to tell if a string is a valid subcommand. # # Regardless of whether an argument is given, the returned expression contains # exactly one parenthesized subexpression for matching the command name. _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\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=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 else printf "\t" if test ${#i} -lt 8; then printf "\t" fi fi done echo ) 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. 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() { local i conf="${gsu_config_file:=${HOME:-}/.$gsu_name.rc}" 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" [[ $? -ne 0 ]] && return ret=-$E_GSU_EDITOR result="${EDITOR:-vi}" "$result" "$conf" [[ $? -ne 0 ]] && return ret=$GSU_SUCCESS return fi for ((i=0; i < ${#gsu_options[@]}; i++)); do local name= option_type= default_value= required= local description= help_text= eval "${gsu_options[$i]}" eval val='"${'${gsu_config_var_prefix}_$name:-'}"' case "$required" in true|yes) printf "# required" ;; *) printf "# optional" ;; esac printf " $option_type: $description" if [[ "$required" != "yes" && "$required" != "true" ]]; then printf " [$default_value]" fi echo [[ -n "$help_text" ]] && sed -e '/^[ ]*$/d; s/^[ ]*/# /g' <<< "$help_text" printf "$name=$val" [[ "$val" == "$default_value" ]] && printf " # default" echo done } complete_prefs() { gsu_complete_options "e" "$@" } export gsu_man_txt=" Print the manual. Usage: man" _com_man() { local equal_signs="==================================================" local minus_signs="--------------------------------------------------" local com num echo "$_gsu_self (_${gsu_banner_txt}_) manual" echo "${equal_signs:0:${#_gsu_self} + ${#gsu_banner_txt} + 16}" echo sed -e '1,/^#\{70,\}/d' -e '/^#\{70,\}/,$d' $0 -e 's/^# *//' echo "----" echo echo "$_gsu_self usage" echo "${minus_signs:0:${#_gsu_self} + 6}" printf "\t" _gsu_usage 2>&1 echo "Each command has its own set of options as described below." echo echo "----" echo echo "Available commands:" _gsu_available_commands 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 echo done ret=$GSU_SUCCESS } _gsu_banner_msg() { gsu_short_msg "### $_gsu_self -- ###" } export gsu_help_txt=" Print online help. 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 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 "--" printf "com_complete()\n$gsu_complete_txt" | head -n 4; echo "--" grep -EA 2 "$ere" $0 } | grep -v -- '--' \ | 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 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" ret=$GSU_SUCCESS return fi if test "$1" = "complete"; then 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 _gsu_print_available_commands result="$1" ret=-$E_GSU_BAD_COMMAND return fi 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 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() { local cmd n cword local -a words if (($# == 0)); then cat < [] # # 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 }