| # Copyright 2015 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| # Routines used to simplify command-line options parsing. |
| |
| # Usage is pretty simple: |
| # |
| # 1) Use 'shell_import option_parser' to import this file. |
| # |
| # 2) Optional: Set PROGRAM_NAME to the name of your program. If unset, the |
| # value of $(program_name) will be used instead. This is only used to |
| # display help. |
| # |
| # 3) Optional: Set PROGRAM_PARAMETERS to a string that describes your |
| # program's parameters (not options). This is only used to display |
| # help. The 'print_help' function actually dumps something like: |
| # |
| # Usage: $PROGRAM_NAME [options] $PROGRAM_PARAMETER |
| # |
| # 4) Optional: Set PROGRAM_DESCRIPTION to a long description of what your |
| # program does. It will be printed just after the 'Usage: ...' line |
| # above. |
| # |
| # 5) Register new options with 'option_register' or 'option_register_var' |
| # as in the following examples: |
| # |
| # option_register --myflag set_myflag "Set custom flag" |
| # option_register_var '--foo=<bar>' FOO "Set foo value to <bar>" |
| # |
| # See the documentation for each function for more details. |
| # |
| # 6) Call 'option_parse', as in: |
| # |
| # option_parse "$@" |
| # |
| # This parses all options and parameters. This also prints relevant |
| # error messages if you specify an invalid option. It also accumulates |
| # all parameters into $PARAM_1, $PARAM_2, $PARAM_3, ... up until |
| # $PARAM_COUNT which is also set by the function. |
| # |
| # --help and -? are automatically registered as options, as well as |
| # --verbose and --quiet so you don't have to deal with these. |
| |
| # NOTE: We translate '-' into '_' when storing the options in global variables |
| # |
| |
| _SHU_OPTIONS="" |
| _SHU_OPTION_FLAGS="" |
| _SHU_OPTION_SETTINGS="" |
| |
| # Return the maximum length of a series of strings |
| # |
| # Usage: len=`max_length <string1> <string2> ...` |
| # |
| _shu_max_length () { |
| printf %s "$@" | tr ' ' '\n' | \ |
| awk 'BEGIN {max=0} {len=length($1); if (len > max) max=len} END {print max}' |
| } |
| |
| # Translate dashes to underscores |
| # Usage: str=`_shu_dashes_to_underscores <values>` |
| _shu_dashes_to_underscores () { |
| printf %s "$@" | tr '-' '_' |
| } |
| |
| # Translate underscores to dashes |
| # Usage: str=`_shu_underscores_to_dashes <values>` |
| _shu_underscores_to_dashes () |
| { |
| printf %s "$@" | tr '_' '-' |
| } |
| |
| # Set a given option attribute |
| # $1: option name |
| # $2: option attribute |
| # $3: attribute value |
| # |
| _shu_option_set_attr () { |
| var_assign _SHU_OPTIONS_$1_$2 "$3" |
| } |
| |
| # Get a given option attribute |
| # $1: option name |
| # $2: option attribute |
| # |
| _shu_option_get_attr () { |
| var_value _SHU_OPTIONS_$1_$2 |
| } |
| |
| # Register a new option |
| # $1: option |
| # $2: small abstract for the option |
| # $3: optional. default value |
| # |
| _shu_register_option_internal () { |
| optlabel= |
| optname= |
| optvalue= |
| opttype= |
| while [ -n "1" ] ; do |
| # Check for something like --setting=<value> |
| if (printf %s "$1" | grep -q -E -e '^--[^=]+=<.+>$'); then |
| optlabel=$(expr -- "$1" : '\(--[^=]*\)=.*') |
| optvalue=$(expr -- "$1" : '--[^=]*=\(<.*>\)') |
| opttype="long_setting" |
| break |
| fi |
| |
| # Check for something like --flag |
| if (printf %s "$1" | grep -q -E -e '^--[^=]+$'); then |
| optlabel="$1" |
| opttype="long_flag" |
| break |
| fi |
| |
| # Check for something like -f<value> |
| if (printf %s "$1" | grep -q -E -e '^-[A-Za-z0-9]<.+>$'); then |
| optlabel=$(expr -- "$1" : '\(-.\).*') |
| optvalue=$(expr -- "$1" : '-.\(<.*>\)') |
| opttype="short_setting" |
| break |
| fi |
| |
| # Check for something like -f |
| if (printf %s "$1" | grep -q -E -e '^-.$'); then |
| optlabel="$1" |
| opttype="short_flag" |
| break |
| fi |
| |
| echo "ERROR: Invalid option format: $1" |
| echo " Check register_option call" |
| exit 1 |
| done |
| |
| log "new option: type='$opttype' name='$optlabel' value='$optvalue'" |
| |
| optname=$(_shu_dashes_to_underscores $optlabel) |
| OPTIONS="$OPTIONS $optname" |
| OPTIONS_TEXT="$OPTIONS_TEXT $1" |
| _shu_option_set_attr $optname label "$optlabel" |
| _shu_option_set_attr $optname otype "$opttype" |
| _shu_option_set_attr $optname value "$optvalue" |
| _shu_option_set_attr $optname text "$1" |
| _shu_option_set_attr $optname abstract "$2" |
| _shu_option_set_attr $optname default "$3" |
| } |
| |
| # Register a new option with a function callback. |
| # |
| # $1: option |
| # $2: name of function that will be called when the option is parsed |
| # $3: small abstract for the option |
| # $4: optional. default value |
| # |
| option_register () { |
| local optname optvalue opttype optlabel |
| _shu_register_option_internal "$1" "$3" "$4" |
| _shu_option_set_attr $optname funcname "$2" |
| } |
| |
| # Register a new option with a variable store |
| # |
| # $1: option |
| # $2: name of variable that will be set by this option |
| # $3: small abstract for the option |
| # |
| # NOTE: The current value of $2 is used as the default |
| # |
| option_register_var () { |
| local optname optvalue opttype optlabel |
| _shu_register_option_internal "$1" "$3" "$(var_value $2)" |
| _shu_option_set_attr $optname varname "$2" |
| } |
| |
| # Print the help, including a list of registered options for this program |
| # Note: Assumes PROGRAM_PARAMETERS and PROGRAM_DESCRIPTION exist and |
| # correspond to the parameters list and the program description |
| # |
| _shu_option_print_help () { |
| local opt text abstract default AWK_SCRIPT |
| |
| if [ -z "$PROGNAME" ]; then |
| PROGNAME=$(program_name) |
| fi |
| |
| echo "Usage: $PROGNAME [options] $PROGRAM_PARAMETERS" |
| echo "" |
| if [ -n "$PROGRAM_DESCRIPTION" ] ; then |
| echo "$PROGRAM_DESCRIPTION" |
| echo "" |
| fi |
| echo "Valid options (defaults are in brackets):" |
| echo "" |
| |
| maxw=$(_shu_max_length "$OPTIONS_TEXT") |
| AWK_SCRIPT=$(echo "{ printf \"%-${maxw}s\", \$1 }") |
| for opt in $OPTIONS; do |
| text=$(_shu_option_get_attr $opt text | awk "$AWK_SCRIPT") |
| abstract=$(_shu_option_get_attr $opt abstract) |
| default=$(_shu_option_get_attr $opt default) |
| if [ -n "$default" ] ; then |
| echo " $text $abstract [$default]" |
| else |
| echo " $text $abstract" |
| fi |
| done |
| echo "" |
| } |
| |
| _shu_option_panic_no_args () { |
| panic "Option '$1' does not take arguments. See --help for usage." |
| } |
| |
| _shu_option_panic_missing_arg () { |
| panic "Option '$1' requires an argument. See --help for usage." |
| } |
| |
| |
| # Parse command-line options and set PARAMETER_COUNT as well as |
| # PARAMETER_1, PARAMETER_2, ... PARAMETER_<n>. |
| # |
| # The caller must have called |option_register| or |option_register_var| |
| # to register specific options. |
| # |
| # $1+: command-line options. |
| option_parse () { |
| local opt optname otype value name fin funcname |
| PARAMETER_COUNT=0 |
| while [ -n "$1" ] ; do |
| # If the parameter does not begin with a dash |
| # it is not an option. |
| param=$(expr -- "$1" : '^\([^\-].*\)$' || true) |
| if [ -n "$param" ] ; then |
| PARAMETER_COUNT=$(( $PARAMETER_COUNT + 1 )) |
| var_assign PARAMETER_$PARAMETER_COUNT "$1" |
| shift |
| continue |
| fi |
| |
| while [ -n "1" ] ; do |
| # Try to match a long setting, i.e. --option=value |
| opt=$(expr -- "$1" : '^\(--[^=]*\)=.*$' || true) |
| if [ -n "$opt" ] ; then |
| otype="long_setting" |
| value=$(expr -- "$1" : '^--[^=]*=\(.*\)$' || true) |
| break |
| fi |
| |
| # Try to match a long flag, i.e. --option |
| opt=$(expr -- "$1" : '^\(--.*\)$' || true) |
| if [ -n "$opt" ] ; then |
| otype="long_flag" |
| value="yes" |
| break |
| fi |
| |
| # Try to match a short setting, i.e. -o<value> |
| opt=$(expr -- "$1" : '^\(-[A-Za-z0-9]\)..*$' || true) |
| if [ -n "$opt" ] ; then |
| otype="short_setting" |
| value=$(expr -- "$1" : '^-.\(.*\)$') |
| break |
| fi |
| |
| # Try to match a short flag, i.e. -o |
| opt=$(expr -- "$1" : '^\(-.\)$' || true) |
| if [ -n "$opt" ] ; then |
| otype="short_flag" |
| value="yes" |
| break |
| fi |
| |
| panic "Unknown option '$1'. Use --help for list of valid values." |
| done |
| |
| #echo "Found opt='$opt' otype='$otype' value='$value'" |
| |
| name=$(_shu_dashes_to_underscores $opt) |
| found=0 |
| for xopt in $OPTIONS; do |
| if [ "$name" != "$xopt" ] ; then |
| continue |
| fi |
| # Check that the type is correct here |
| # |
| # This also allows us to handle -o <value> as -o<value> |
| # |
| xotype=$(_shu_option_get_attr $name otype) |
| if [ "$otype" != "$xotype" ] ; then |
| case "$xotype" in |
| "short_flag") |
| _shu_option_panic_no_args "$opt" |
| ;; |
| "short_setting") |
| if [ -z "$2" ] ; then |
| _shu_option_panic_missing_arg "$opt" |
| fi |
| value="$2" |
| shift |
| ;; |
| "long_flag") |
| _shu_option_panic_no_args "$opt" |
| ;; |
| "long_setting") |
| _shu_option_panic_missing_arg "$opt" |
| ;; |
| esac |
| fi |
| found=1 |
| break |
| break |
| done |
| if [ "$found" = "0" ] ; then |
| panic "Unknown option '$opt'. See --help for usage." |
| fi |
| # Set variable or launch option-specific function. |
| varname=$(_shu_option_get_attr $name varname) |
| if [ -n "$varname" ] ; then |
| var_assign $varname "$value" |
| else |
| eval $(_shu_option_get_attr $name funcname) \'"$value"\' |
| fi |
| shift |
| done |
| } |
| |
| |
| _shu_do_option_help () { |
| _shu_option_print_help |
| exit 0 |
| } |
| |
| option_register "--help" _shu_do_option_help "Print this help." |
| option_register "--verbose" increment_verbosity "Increment verbosity." |
| option_register "--verbosity=<level>" set_verbosity "Set verbosity level." |
| option_register "--quiet" decrement_verbosity "Decrement verbosity." |