From d84fb61667757b1e62b915781357e10693ce8a9e Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Fri, 23 Sep 2011 14:03:38 +0200 Subject: [PATCH] Split gsu. This patch splits the gsu file into two files for config parsing and subcommand handling respectively. Each part may be used independently of the other. Moreover, all scripts which use gsu now include the needed parts directly rather than relying on the shell to have already sourced the needed gsu files. This shortens bash startup time and makes gsu easier to use for people whose login shell is not bash. --- misc/gsu/common | 95 +++++++++++ misc/gsu/config | 88 +++++++++++ funcs/gsu => misc/gsu/subcommand | 260 ++++++------------------------- 3 files changed, 231 insertions(+), 212 deletions(-) create mode 100644 misc/gsu/common create mode 100644 misc/gsu/config rename funcs/gsu => misc/gsu/subcommand (68%) diff --git a/misc/gsu/common b/misc/gsu/common new file mode 100644 index 0000000..63b2ab6 --- /dev/null +++ b/misc/gsu/common @@ -0,0 +1,95 @@ +#!/bin/bash +# (C) 2006-2011 Andre Noll + +_gsu_init_errors() +{ + gsu_errors=" +GSU_SUCCESS success +E_GSU_BAD_COMMAND invalid command +E_GSU_NOT_A_NUMBER not a number +E_GSU_BAD_CONFIG_VAR invalid config variable +E_GSU_NEED_VALUE value required but not given +E_GSU_BAD_BOOL bad value for boolian option +E_GSU_BAD_OPTION_TYPE invalid option type +E_GSU_BAD_ARG_COUNT invalid number of arguments +E_GSU_EDITOR failed to execute editor +E_GSU_MKDIR failed to create directory +E_GSU_GETOPTS getopts error +$gsu_errors +" + local a b i=0 + while read a b; do + if test -z "$a"; then + continue + fi + #echo "a:$a, b: $b" + gsu_error_txt[i]="$b" + eval $a=$i + i=$(($i + 1)) + done << EOF + $gsu_errors +EOF +} +export -f _gsu_init_errors + +# check if $1 is a number +gsu_is_a_number() +{ + result="$1" + if test "$1" -eq "$1" &> /dev/null; then + ret=$GSU_SUCCESS + else + ret=-$E_GSU_NOT_A_NUMBER + fi +} +export -f gsu_is_a_number + +gsu_short_msg() +{ + echo "$1" 1>&2 +} +export -f gsu_short_msg + +gsu_msg() +{ + gsu_short_msg "$_gsu_self: $1" +} +export -f gsu_msg + +gsu_date_msg() +{ + gsu_short_msg "$_gsu_self $(date): $1" +} +export -f gsu_date_msg + +gsu_err_msg() +{ + local txt="$result" err + + gsu_is_a_number "$ret" + if test $ret -lt 0; then + gsu_msg "unknown error ($ret:$txt)" + exit 1 + fi + if test $result -ge 0; then + gsu_msg "unknown error ($result:$txt)" + exit 1 + fi + err=$((0 - $result)) + if test -n "$txt"; then + txt="$txt: ${gsu_error_txt[$err]}" + else + txt="${gsu_error_txt[$err]}" + fi + gsu_msg "$txt" +} +export -f gsu_err_msg + +_gsu_setup() +{ + _gsu_self="$(basename $0)" + gsu_name="${gsu_name:=$_gsu_self}" + gsu_config_var_prefix="${gsu_config_var_prefix:=$gsu_name}" + _gsu_init_errors +} +export -f _gsu_setup diff --git a/misc/gsu/config b/misc/gsu/config new file mode 100644 index 0000000..df96af2 --- /dev/null +++ b/misc/gsu/config @@ -0,0 +1,88 @@ +#!/bin/bash + +# Syntactically check the gsu_options array for errors and parse the config +# file. +gsu_check_options() +{ + local i conf="${gsu_config_file:=$HOME/.$gsu_name.rc}" val + + for ((i=0; i < ${#gsu_options[@]}; i++)); do + eval "${gsu_options[$i]}" + eval val='"'\$$name'"' + eval orig_${gsu_config_var_prefix}_$name='"'${val}'"' + done + + [[ -r "$conf" ]] && source "$conf" + + for ((i=0; i < ${#gsu_options[@]}; i++)); do + local name= option_type= default_value= required= + local description= help_text= + local val orig_val + + eval "${gsu_options[$i]}" + + + # Check name. It must be non-empty and consist of [a-zA-Z_0-9] + # only. Moreover it must not start with [a-zA-Z]. + + ret=-$E_GSU_BAD_CONFIG_VAR + result="name: '$name'" + # bash's =~ works only for 3.2 and newer, so use grep + echo "$name" | grep '^[a-zA-Z][a-zA-Z_0123456789]*$' &> /dev/null; + [[ $? -ne 0 ]] && return + + eval orig_val='"'\$orig_${gsu_config_var_prefix}_$name'"' + if [[ -z "$orig_val" ]]; then + eval val='"'\$$name'"' + else + val="$orig_val" + fi + case "$required" in + true|yes) + ret=-$E_GSU_NEED_VALUE + result="$name" + [[ -z "$val" ]] && return + ;; + false|no) + ;; + *) + ret=-$E_GSU_BAD_BOOL + result="required: $required, name: $name, val: $val" + return + esac + + eval ${gsu_config_var_prefix}_$name='"'\${val:=$default_value}'"' + # Check option type. ATM, only num and string are supported + # Other types may be added without breaking compatibility + case "$option_type" in + string) + ;; + num) + gsu_is_a_number "$val" + [[ $ret -lt 0 ]] && return + ;; + *) + ret=-$E_GSU_BAD_OPTION_TYPE + result="$name/$option_type" + return + esac + done + ret=$GSU_SUCCESS +} +export -f gsu_check_options + +# Call gsu_check_options(), die on errors. +gsu_check_options_or_die() +{ + gsu_check_options + if (($ret < 0)); then + gsu_err_msg + exit 1 + fi +} + +if [[ "$(type -t _gsu_setup)" != "function" ]]; then + gsu_dir=${gsu_dir:=$HOME/.gsu} + . $gsu_dir/common || exit 1 + _gsu_setup +fi diff --git a/funcs/gsu b/misc/gsu/subcommand similarity index 68% rename from funcs/gsu rename to misc/gsu/subcommand index 1a35fca..332e5a0 100644 --- a/funcs/gsu +++ b/misc/gsu/subcommand @@ -1,135 +1,10 @@ #!/bin/bash -# gsu -- the global subcommand utility # (C) 2006-2011 Andre Noll -_gsu_init_errors() -{ - gsu_errors=" -GSU_SUCCESS success -E_GSU_BAD_COMMAND invalid command -E_GSU_NOT_A_NUMBER not a number -E_GSU_BAD_CONFIG_VAR invalid config variable -E_GSU_NEED_VALUE value required but not given -E_GSU_BAD_BOOL bad value for boolian option -E_GSU_BAD_OPTION_TYPE invalid option type -E_GSU_BAD_ARG_COUNT invalid number of arguments -E_GSU_EDITOR failed to execute editor -E_GSU_MKDIR failed to create directory -E_GSU_GETOPTS getopts error -$gsu_errors -" - local a b i=0 - while read a b; do - if test -z "$a"; then - continue - fi - #echo "a:$a, b: $b" - gsu_error_txt[i]="$b" - eval $a=$i - i=$(($i + 1)) - done << EOF - $gsu_errors -EOF -} -export -f _gsu_init_errors - -# check if $1 is a number -gsu_is_a_number() -{ - result="$1" - if test "$1" -eq "$1" &> /dev/null; then - ret=$GSU_SUCCESS - else - ret=-$E_GSU_NOT_A_NUMBER - fi -} -export -f gsu_is_a_number - -# 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 -} -export -f gsu_check_arg_count - -gsu_short_msg() -{ - echo "$1" 1>&2 -} -export -f gsu_short_msg - -gsu_msg() -{ - gsu_short_msg "$_gsu_self: $1" -} -export -f gsu_msg - -gsu_date_msg() -{ - gsu_short_msg "$_gsu_self $(date): $1" -} -export -f gsu_date_msg - - - -_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 ###" -} -export -f _gsu_banner_msg - -gsu_err_msg() -{ - local txt="$result" err - - gsu_is_a_number "$ret" - if test $ret -lt 0; then - gsu_msg "unknown error ($ret:$txt)" - exit 1 - fi - if test $result -ge 0; then - gsu_msg "unknown error ($result:$txt)" - exit 1 - fi - err=$((0 - $result)) - if test -n "$txt"; then - txt="$txt: ${gsu_error_txt[$err]}" - else - txt="${gsu_error_txt[$err]}" - fi - gsu_msg "$txt" -} -export -f gsu_err_msg +if [[ $(type -t gsu_is_a_number) != "function" ]]; then + GSU_DIR=${GSU_DIR:=$HOME/.gsu} + . $GSU_DIR/common || exit 1 +fi _gsu_usage() { @@ -266,6 +141,18 @@ _com_man() } export -f _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 ###" +} +export -f _gsu_banner_msg + export gsu_help_txt=" Print online help. @@ -330,77 +217,6 @@ _com_help() } export -f _com_help -# internal gsu function that syntactically checks the gsu_options array -# for errors and parses the config file. -_gsu_check_options() -{ - local i conf="${gsu_config_file:=$HOME/.$gsu_name.rc}" val - - for ((i=0; i < ${#gsu_options[@]}; i++)); do - eval "${gsu_options[$i]}" - eval val='"'\$$name'"' - eval orig_${gsu_config_var_prefix}_$name='"'${val}'"' - done - - [[ -r "$conf" ]] && source "$conf" - - for ((i=0; i < ${#gsu_options[@]}; i++)); do - local name= option_type= default_value= required= - local description= help_text= - local val orig_val - - eval "${gsu_options[$i]}" - - - # Check name. It must be non-empty and consist of [a-zA-Z_0-9] - # only. Moreover it must not start with [a-zA-Z]. - - ret=-$E_GSU_BAD_CONFIG_VAR - result="name: '$name'" - # bash's =~ works only for 3.2 and newer, so use grep - echo "$name" | grep '^[a-zA-Z][a-zA-Z_0123456789]*$' &> /dev/null; - [[ $? -ne 0 ]] && return - - eval orig_val='"'\$orig_${gsu_config_var_prefix}_$name'"' - if [[ -z "$orig_val" ]]; then - eval val='"'\$$name'"' - else - val="$orig_val" - fi - case "$required" in - true|yes) - ret=-$E_GSU_NEED_VALUE - result="$name" - [[ -z "$val" ]] && return - ;; - false|no) - ;; - *) - ret=-$E_GSU_BAD_BOOL - result="required: $required, name: $name, val: $val" - return - esac - - eval ${gsu_config_var_prefix}_$name='"'\${val:=$default_value}'"' - # Check option type. ATM, only num and string are supported - # Other types may be added without breaking compatibility - case "$option_type" in - string) - ;; - num) - gsu_is_a_number "$val" - [[ $ret -lt 0 ]] && return - ;; - *) - ret=-$E_GSU_BAD_OPTION_TYPE - result="$name/$option_type" - return - esac - done - ret=$GSU_SUCCESS -} -export -f _gsu_check_options - # Wrapper for bash's getopts. # # Aborts on programming errors such as missing or invalid option string. On @@ -491,17 +307,7 @@ export -f gsu_getopts gsu() { local i - _gsu_self="$(basename $0)" - gsu_name="${gsu_name:=$_gsu_self}" - gsu_config_var_prefix="${gsu_config_var_prefix:=$gsu_name}" - _gsu_init_errors - _gsu_check_options - if [[ "$ret" -lt 0 ]]; then - if [[ "$1" != "help" && "$1" != "man" ]]; then - gsu_err_msg - exit 1 - fi - fi + _gsu_setup _gsu_available_commands gsu_cmds="$result" if test $# -eq 0; then @@ -541,4 +347,34 @@ gsu() } export -f gsu -# TODO: gsu_strerror: get error string +# 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 +} +export -f gsu_check_arg_count + -- 2.30.2