Commit f8698895 authored by Emeric Verschuur's avatar Emeric Verschuur
Browse files

add persistant settings and a lot of comments :)

parent 91872a73
Pipeline #44 passed with stage
in 12 seconds
......@@ -44,6 +44,7 @@ trap 'bashopts_exit_handle' ERR
# expansions and subshells
set -o errtrace
# display a error (fatal) error
bashopts_error() {
local l
>&2 echo -n "[ERROR] "
......@@ -53,6 +54,7 @@ bashopts_error() {
exit 1
}
# display a warning (non fatal) error
bashopts_warning() {
local l
>&2 echo -n "[WARNING] "
......@@ -65,26 +67,31 @@ bashopts_regex_escape () {
echo $1 | sed 's/[][()\.^$\/?*+]/\\&/g'
}
# check if the value is a boolean
bashopts_isboolean() {
[[ $1 =~ ^(true|false)$ ]] || return 1
}
# check if the value is a number
bashopts_isnumber() {
[[ $1 =~ ^-?[0-9]+([.][0-9]+)?$ ]] || return 1
}
# extract the value part of a declaration ("the value")
bashopts_get_def() {
declare | grep "^$1=" | sed -E 's/^[^=]+=//g'
# NOTE: alternative but not working in some case...:
# declare -p $1 | sed -E "s/^declare\\s[^=]*=//g"
}
# extract the full declaration (name="the value")
bashopts_get_def_full() {
declare | grep "^$1="
# NOTE: alternative but not working in some case...:
# declare -p $1 | sed -E "s/^declare\\s[^=]*=/$1=/g"
}
# check and format an option name value
bashopts_chkvarname() {
if [[ $1 =~ ^[a-zA-Z0-9_]+$ ]]; then
echo $1
......@@ -93,6 +100,7 @@ bashopts_chkvarname() {
bashopts_error "$1 is not a valid variable name"
}
# check and format a number value
bashopts_chknum() {
if [ -z "$1" ]; then
echo 0
......@@ -104,6 +112,7 @@ bashopts_chknum() {
bashopts_error "$1 is not a valid number"
}
# check and format a boolean value
bashopts_chkbool() {
case "$1" in
''|f|false|F|FALSE|0)
......@@ -118,29 +127,37 @@ bashopts_chkbool() {
esac
}
# declare the options property arrays
for f in name default expression short_opt long_opt description type method setting interactive req_value; do
eval declare -x -A bashopts_optprop_$f
done
declare -x -A bashopts_opt2name
# declare the associative array: arg name => option name
declare -x -A bashopts_arg2op
# option list in the declaration order
bashopts_optlist=()
# commands list (from global tool_name [args] [commands] [-- optional extra args])
bashopts_commands=()
# extra arguments list (from global tool_name [args] [commands] [-- optional extra args])
bashopts_extra_args=()
# tool name (from global tool_name [args] [commands] [-- optional extra args])
bashopts_tool_name=$0
# STEP 1: setup
bashopts_setup() {
local arg arglist no_default_opts
if ! arglist=$(getopt -o "n:d:u:s:f" -n "$0 " -- "$@"); then
if ! arglist=$(getopt -o "n:d:u:s:fp" -n "$0 " -- "$@"); then
bashopts_error "Usage bashopts_setup:" \
" -n <val> Tool name" \
" -d <val> Tool description" \
" -u <val> Tool usage description" \
" -s <val> setting file path" \
" -f Force edition if ineractive options (default: only if not defined via the cmd line)"
" -p Force value storage even if the value is equal to the default one"
return 1
fi
eval set -- "$arglist";
# Store the global bashopts properties
while true; do
arg=$1
shift
......@@ -150,6 +167,7 @@ bashopts_setup() {
-u) bashopts_tool_usage=$1; shift;;
-s) bashopts_tool_settings_path=$1; shift;;
-f) bashopts_tool_force_edit="true";;
-p) bashopts_tool_settings_force_write="true";;
--) break;;
*) bashopts_error "Fatal error";;
esac
......@@ -161,6 +179,7 @@ bashopts_setup() {
bashopts_error "Undefined tool description"
fi
bashopts_tool_usage=${bashopts_tool_usage:-"$bashopts_tool_name [options and commands] [-- [extra args]]"}
# add the default options
if [ "$no_default_opts" != "true" ]; then
bashopts_declare -n __bashopts_display_help__ -l help -o h -d "Display this help"
bashopts_declare -n bashopts_non_interactive -l non-interactive -o n -d "Non interactive mode"
......@@ -187,6 +206,7 @@ bashopts_declare() {
fi
eval set -- "$arglist";
declare -A options
# parse all the parameters
while true; do
arg=$1
shift
......@@ -206,6 +226,7 @@ bashopts_declare() {
*) bashopts_error "Fatal error";;
esac
done
# format the type and check/format the default value
case "${options[type]}" in
''|bool|boolean)
options[type]="boolean"
......@@ -224,58 +245,69 @@ bashopts_declare() {
bashopts_error "Invalid type ${options[type]}"
;;
esac
# format the option method
case "${options[method]}" in
''|s|set)
# default: simple value - override
options[method]="set"
;;
a|add)
# array value - add
options[method]="add"
;;
*)
bashopts_error "Invalid method ${options[method]}"
;;
esac
# Check option name
if [[ -v bashopts_optprop_name[${options[name]}] ]]; then
bashopts_error "Dupplicate option name '${options[name]}'"
fi
# check the short option
if [[ -v options[short_opt] ]]; then
if ! [[ ${options[short_opt]} =~ ^[a-zA-Z0-9_-]$ ]]; then
bashopts_error "Invalid short option ${options[short_opt]}"
fi
if [[ -v bashopts_opt2name[-${options[short_opt]}] ]]; then
if [[ -v bashopts_arg2op[-${options[short_opt]}] ]]; then
bashopts_error "Dupplicate short option '${options[short_opt]}'"
fi
bashopts_opt2name[-${options[short_opt]}]=${options[name]}
bashopts_arg2op[-${options[short_opt]}]=${options[name]}
fi
# check the long option
if [[ -v options[long_opt] ]]; then
if ! [[ ${options[long_opt]} =~ ^[a-zA-Z0-9_-]{2,}$ ]]; then
bashopts_error "Invalid long option ${options[long_opt]}"
fi
if [[ -v bashopts_opt2name[--${options[long_opt]}] ]]; then
if [[ -v bashopts_arg2op[--${options[long_opt]}] ]]; then
bashopts_error "Dupplicate long option '${options[long_opt]}'"
fi
bashopts_opt2name[--${options[long_opt]}]=${options[name]}
bashopts_arg2op[--${options[long_opt]}]=${options[name]}
fi
# store the option properties
for f in ${!options[@]}; do
eval "bashopts_optprop_$f[${options[name]}]='${options[$f]//\'/\'\\\'\'}'"
done
bashopts_optlist+=(${options[name]})
}
# maximum of two values
bashopts_math_max() {
echo $(($1>$2?$1:$2))
}
# minimum of two values
bashopts_math_min() {
echo $(($1<$2?$1:$2))
}
# join array element
bashopts_join_by() {
local IFS="$1"
shift
shift || bashopts_error "Usage: bashopts_join_by <character> [elt1 [elt2...]]"
echo "$*"
}
# dump an option value by its name
bashopts_dump_value() {
local op=$1
shift || bashopts_error "Usage: bashopts_dump_value op_name"
......@@ -307,9 +339,11 @@ bashopts_dump_value() {
echo -n "]"
}
# display the formated help
bashopts_diplay_help() {
local elts optargs_max_len=8 val dval ncol=$COLUMNS
declare -A optargs
# compute the good arguments comumn size
for op in "${bashopts_optlist[@]}"; do
elts=()
unset val
......@@ -323,7 +357,7 @@ bashopts_diplay_help() {
done
ncol=${ncol:-160}
optargs_max_len=$(bashopts_math_min $optargs_max_len $(( $ncol / 3 )) )
set +x
# display global info
echo
echo "NAME:"
echo " $bashopts_tool_name - $bashopts_tool_description"
......@@ -336,21 +370,26 @@ bashopts_diplay_help() {
elts=""
dval=""
if ! [[ $op =~ ^__.*__$ ]]; then
if [[ -v bashopts_optprop_expression[$op] ]]; then
dval="\"${bashopts_optprop_expression[$op]//\"/\\\"}\""
fi
if [ "${bashopts_optprop_type[$op]}" == "string" ]; then
dval=${dval:-"\"${bashopts_optprop_default[$op]//\"/\\\"}\""}
else
dval=${dval:-${bashopts_optprop_default[$op]}}
fi
# display additional information the each properties
# discarding special options like --help
if [[ -v bashopts_optprop_expression[$op] ]]; then
dval="\"${bashopts_optprop_expression[$op]//\"/\\\"}\""
fi
if [ "${bashopts_optprop_type[$op]}" == "string" ]; then
dval=${dval:-"\"${bashopts_optprop_default[$op]//\"/\\\"}\""}
else
dval=${dval:-${bashopts_optprop_default[$op]}}
fi
# add type and default value info
elts="- [\$$op] (type:${bashopts_optprop_type[$op]}, default:$dval)"
fi
# display arguments, value if available, description, and additional info if available
printf " %-${optargs_max_len}s %s\n" "${optargs[$op]}" "${bashopts_optprop_description[$op]} $elts"
done
test "$1" != "-e" || exit $2
}
# display all otions values and properties
bashopts_diplay_summary() {
local elts desc_max_len=0 val dval
declare -A optargs
......@@ -368,6 +407,7 @@ bashopts_diplay_summary() {
bashopts_parse_args() {
local op arg val args is_arg short_opts long_opts
# split argument into two arrays: normal and extra arguments
is_arg=1
args=()
for arg in "$@"; do
......@@ -379,6 +419,7 @@ bashopts_parse_args() {
fi
done
# build the long and short getopt option list from the options
short_opts=""
long_opts=()
for op in "${bashopts_optlist[@]}"; do
......@@ -391,45 +432,55 @@ bashopts_parse_args() {
done
long_opts=$(bashopts_join_by , ${long_opts[@]})
# call the getopt
if ! args=$(getopt -o $short_opts -l "$long_opts" -n "$bashopts_tool_name" -- "${args[@]}"); then
>&2 bashopts_diplay_help
exit 1
fi
eval set -- "$args";
# store the arguments value part
while true; do
arg=$1
shift
case $arg in
--)
# end of the argument part
break
;;
-*)
val="$1"
shift
op=${bashopts_opt2name[$arg]}
op=${bashopts_arg2op[$arg]}
if [ -z "$val" ] && [ "${bashopts_optprop_type[$op]}" != "boolean" ]; then
# empty value tell to unset the value or clear the array
unset $op
continue
fi
case "${bashopts_optprop_type[$op]}" in
boolean)
if [ -z "$val" ]; then
# boolean argument with no value is considered as true
val="true"
fi
# check and format boolean value
val=$(bashopts_chkbool $val)
;;
string)
# nothing to do
;;
number)
# check and format number value
val=$(bashopts_chknum $val)
;;
esac
case "${bashopts_optprop_method[$op]}" in
set)
# normal case: override the value
eval "$op=$(declare -p val | sed -E 's/^declare\s[^=]*=//g')"
;;
add)
# array case: add the value
eval "$op+=($(declare -p val | sed -E 's/^declare\s[^=]*=//g'))"
;;
esac
......@@ -439,9 +490,12 @@ bashopts_parse_args() {
;;
esac
done
# store the command part
bashopts_commands=("$@")
}
# display an array: [val1, val2, ...]
bashopts_dump_array() {
local type=$1
shift || bashopts_error "Usage: bashopts_dump_array type elt1 [elt2...]"
......@@ -482,7 +536,7 @@ bashopts_process_args() {
if ! [[ -v $op ]] || [ "$bashopts_tool_force_edit" == "true" ]; then
unset tval
# read the value from file
if ! [[ -v $op ]] && [ "${bashopts_optprop_setting[$op]}" == "true" ] \
if ! [[ -v $op ]] && [ "${bashopts_optprop_setting[$op]}" == "true" ] \
&& [ -f "$(readlink -f $bashopts_tool_settings_path)" ] \
&& grep -E -q "^$op=" $bashopts_tool_settings_path; then
eval "tval=$(grep -E "^${op}=" $bashopts_tool_settings_path | sed -E "s/^[^=]+=//g")"
......@@ -492,22 +546,27 @@ bashopts_process_args() {
eval "tval=$(bashopts_get_def dval)"
fi
if [ "${bashopts_optprop_interactive[$op]}" == "true" ] && [ "$bashopts_non_interactive" != "true" ]; then
# interactive edition
while true; do
echo "* ${bashopts_optprop_description[$op]}"
echo -n " $(bashopts_dump_array {bashopts_optprop_type[$op]} "${tval[@]}"): "
read ival
if [ -z "$ival" ]; then
# keep the previous/default value
break;
fi
if [ "${bashopts_optprop_method[$op]}" == "add" ]; then
# array value
if ! eval "tval=$ival" 2>/dev/null; then
echo "'$ival' is not a valid BASH array (format: '( \"val 1\" \"val2\" \"val3...\" )')"
unset tval
continue
fi
else
# non array/norman value
tval=$ival
fi
# check boolean/number (array or not)
case "${bashopts_optprop_type[$op]}" in
boolean)
for v in "${tval[@]}"; do
......@@ -529,8 +588,10 @@ bashopts_process_args() {
;;
esac
if [[ -v tval ]]; then
# edit OK, break
break
fi
# otherwise, loop...
done
fi
if [[ -v tval ]]; then
......@@ -542,9 +603,13 @@ bashopts_process_args() {
# vrite the value to the setting file
(
if [ -f "$bashopts_tool_settings_path" ]; then
# remove old value
sed -i "/^$op=/d" $bashopts_tool_settings_path
fi
if [ "$(bashopts_get_def $op)" != "$(bashopts_get_def dval)" ]; then
if [ "$bashopts_tool_settings_force_write" == "true" ] \
|| [ "$(bashopts_get_def $op)" != "$(bashopts_get_def dval)" ]; then
# append the new value to the file if the value is not the default or
# force_write is true
echo "$(bashopts_get_def_full $op)" >> $bashopts_tool_settings_path
fi
) || bashopts_warning "Please check the settings file"
......
......@@ -247,6 +247,19 @@ _test_case_16() {
fi
}
_test_case_17() {
rm -f /tmp/bashopts_testrc
bashopts_setup -n "$0" -d "Test case $0" -s /tmp/bashopts_testrc -p
bashopts_declare -n a_value -l value -d "A value" -s -i -t string -v "default value"
bashopts_parse_args
bashopts_process_args > /dev/null <<< ""
req_test_eq "$a_value" "default value"
req_test_eq "$(cat /tmp/bashopts_testrc)" "a_value='default value'"
}
if [ ${#} -eq 0 ]; then
for t in $(grep -E -o '_test_case_\w+\>' $0); do
echo "=> Test case $t"
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment