Commit 6aa827ec authored by Emeric Verschuur's avatar Emeric Verschuur

add bashopts_setup & bashopts_process_args

parent 88a03a8b
stages:
- test
tests:
image: ubuntu:16.04
script:
- for t in $(grep -E -o '_test_case_(\w+)\>' ./test.sh); do
./test.sh $t;
done
stage: test
......@@ -94,15 +94,56 @@ bashopts_chkbool() {
esac
}
for f in name default short_opt long_opt description type method settings req_value; do
eval declare -x -A bashopts_f_$f
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
bashopts_optlist=()
bashopts_extra_args=()
bashopts_app_name=$0
bashopts_tool_name=$0
bashopts_tool_settings_path="/dev/null"
# STEP 1: setup
bashopts_setup() {
local arg arglist no_default_opts
if ! arglist=$(getopt -o "n:d:u:s:f" -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)"
return 1
fi
eval set -- "$arglist";
while true; do
arg=$1
shift
case "$arg" in
-n) bashopts_tool_name=$1; shift;;
-d) bashopts_tool_description=$1; shift;;
-u) bashopts_tool_usage=$1; shift;;
-s) bashopts_tool_settings_path=$1; shift;;
-f) bashopts_tool_force_edit="true";;
--) break;;
*) bashopts_error "Fatal error";;
esac
done
if [ -z "$bashopts_tool_name" ]; then
bashopts_error "Undefined tool name"
fi
if [ -z "$bashopts_tool_description" ]; then
bashopts_error "Undefined tool description"
fi
bashopts_tool_usage=${bashopts_tool_usage:-"[options and commands] [-- [extra args]]"}
if [ "$no_default_opts" != "true" ]; then
bashopts_add -n __bashopts_display_help__ -l help -o h -d "Display this help"
bashopts_add -n bashopts_non_interactive -l non-interactive -o n -d "Non interactive mode"
fi
}
# STEP 2: add options
bashopts_add() {
local arg arglist options
if ! arglist=$(getopt -o "n:v:e:o:l:d:t:m:rsi" -n "$0 " -- "$@"); then
......@@ -117,7 +158,7 @@ bashopts_add() {
" -m <val> Method: set (DEFAULT: simple value), add (list with several values)" \
" -r Value required required" \
" -i Enable interactive edition" \
" -s Store in settings"
" -s Store in setting"
return 1
fi
eval set -- "$arglist";
......@@ -134,7 +175,7 @@ bashopts_add() {
-d) options[description]=$1; shift;;
-t) options[type]=$1; shift;;
-m) options[method]=$1; shift;;
-s) options[settings]="true";;
-s) options[setting]="true";;
-i) options[interactive]="true";;
-r) options[req_value]="true";;
--) break;;
......@@ -173,7 +214,7 @@ bashopts_add() {
bashopts_error "Invalid method ${options[method]}"
;;
esac
if [[ -v bashopts_f_name[${options[name]}] ]]; then
if [[ -v bashopts_optprop_name[${options[name]}] ]]; then
bashopts_error "Dupplicate option name '${options[name]}'"
fi
if [[ -v options[short_opt] ]]; then
......@@ -195,7 +236,7 @@ bashopts_add() {
bashopts_opt2name[--${options[long_opt]}]=${options[name]}
fi
for f in ${!options[@]}; do
eval bashopts_f_$f[${options[name]}]=\'${options[$f]//\'/\'\\\'\'}\'
eval bashopts_optprop_$f[${options[name]}]=\'${options[$f]//\'/\'\\\'\'}\'
done
bashopts_optlist+=(${options[name]})
}
......@@ -215,48 +256,40 @@ bashopts_diplay_help() {
declare -A optargs
for op in "${bashopts_optlist[@]}"; do
elts=()
if [[ -v bashopts_f_short_opt[$op] ]]; then elts+=("-${bashopts_f_short_opt[$op]}"); fi
if [[ -v bashopts_f_long_opt[$op] ]]; then elts+=("--${bashopts_f_long_opt[$op]}"); fi
if [[ -v bashopts_optprop_short_opt[$op] ]]; then elts+=("-${bashopts_optprop_short_opt[$op]}"); fi
if [[ -v bashopts_optprop_long_opt[$op] ]]; then elts+=("--${bashopts_optprop_long_opt[$op]}"); fi
optargs[$op]=$(bashopts_join_by , ${elts[@]})
optargs_max_len=$(bashopts_math_max $optargs_max_len ${#optargs[$op]})
done
echo
echo "NAME:"
echo " $bashopts_app_name - $bashopts_app_description"
echo " $bashopts_tool_name - $bashopts_tool_description"
echo
echo "USAGE:"
echo -e " $bashopts_app_usage"
echo -e " $bashopts_tool_usage"
echo
echo "OPTIONS:"
for op in "${bashopts_optlist[@]}"; do
elts=""
dval=""
if ! [[ $op =~ ^__.*__$ ]] && [[ -v $op ]]; then
if [ "${bashopts_f_type[$op]}" == "string" ]; then
if [[ -v bashopts_optprop_expression[$op] ]]; then
dval="\"${bashopts_optprop_expression[$op]//\"/\\\"}\""
fi
if [ "${bashopts_optprop_type[$op]}" == "string" ]; then
val="\"${!op//\"/\\\"}\""
dval="\"${bashopts_f_default[$op]//\"/\\\"}\""
dval=${dval:-"\"${bashopts_optprop_default[$op]//\"/\\\"}\""}
else
val=${!op}
dval=${bashopts_f_default[$op]}
dval=${dval:-${bashopts_optprop_default[$op]}}
fi
elts="- ${bashopts_f_type[$op]} $op=$val (default:$dval)"
elts="- ${bashopts_optprop_type[$op]} $op=$val (default:$dval)"
fi
printf " %-${optargs_max_len}s %s\n" ${optargs[$op]} "${bashopts_f_description[$op]} $elts"
printf " %-${optargs_max_len}s %s\n" ${optargs[$op]} "${bashopts_optprop_description[$op]} $elts"
done
}
bashopts_setup() {
if [ "$#" -lt 2 ]; then
bashopts_error "Usage: bashopts_setup <appt_name> <app_description> [command usage line(s)]"
fi
bashopts_app_name=$1
bashopts_app_description=$2
bashopts_app_usage=${3:-"[options and commands] [-- [extra args]]"}
bashopts_add -n __bashopts_display_help__ -l help -o h -d "Display this help"
}
bashopts_set_app_name() {
bashopts_app_name=$1
}
# STEP 3: parse arg
bashopts_parse_args() {
local op arg val args is_arg short_opts long_opts
......@@ -274,16 +307,16 @@ bashopts_parse_args() {
short_opts=""
long_opts=()
for op in "${bashopts_optlist[@]}"; do
if [[ -v bashopts_f_short_opt[$op] ]]; then
short_opts="${short_opts}${bashopts_f_short_opt[$op]}:$(test "${bashopts_f_req_value[$op]}" == "true" || echo ":")"
if [[ -v bashopts_optprop_short_opt[$op] ]]; then
short_opts="${short_opts}${bashopts_optprop_short_opt[$op]}:$(test "${bashopts_optprop_req_value[$op]}" == "true" || echo ":")"
fi
if [[ -v bashopts_f_long_opt[$op] ]]; then
long_opts+=("${bashopts_f_long_opt[$op]}:$(test "${bashopts_f_req_value[$op]}" == "true" || echo ":")")
if [[ -v bashopts_optprop_long_opt[$op] ]]; then
long_opts+=("${bashopts_optprop_long_opt[$op]}:$(test "${bashopts_optprop_req_value[$op]}" == "true" || echo ":")")
fi
done
long_opts=$(bashopts_join_by , ${long_opts[@]})
if ! args=$(getopt -o $short_opts -l "$long_opts" -n "$bashopts_app_name " -- "${args[@]}"); then
if ! args=$(getopt -o $short_opts -l "$long_opts" -n "$bashopts_tool_name " -- "${args[@]}"); then
>&2 bashopts_diplay_help
exit 1
fi
......@@ -300,7 +333,7 @@ bashopts_parse_args() {
val=$1
shift
op=${bashopts_opt2name[$arg]}
case "${bashopts_f_type[$op]}" in
case "${bashopts_optprop_type[$op]}" in
boolean)
if [ -z "$val" ]; then
val="true"
......@@ -313,12 +346,12 @@ bashopts_parse_args() {
val=$(bashopts_chknum $val)
;;
esac
case "${bashopts_f_method[$op]}" in
case "${bashopts_optprop_method[$op]}" in
set)
eval "$op='${val//\'/\'\\\'\'}'"
eval "$op=$(declare -p val | sed -E 's/^declare\s[^=]*=//g')"
;;
add)
eval "$op+=('${val//\'/\'\\\'\'}')"
eval "$op+=($(declare -p val | sed -E 's/^declare\s[^=]*=//g'))"
;;
esac
;;
......@@ -327,10 +360,52 @@ bashopts_parse_args() {
;;
esac
done
}
# STEP 4: process arg
bashopts_process_args() {
local dval ival
touch $bashopts_tool_settings_path
if [ "$__bashopts_display_help__" == "true" ]; then
bashopts_non_interactive="true"
fi
for op in "${bashopts_optlist[@]}"; do
# eval or get default value
if [[ -v bashopts_optprop_expression[$op] ]]; then
eval "dval=${bashopts_optprop_expression[$op]}"
else
dval=${bashopts_optprop_default[$op]}
fi
if ! [[ -v $op ]] || [ "$bashopts_tool_force_edit" == "true" ]; then
unset tval
# read the value from file
if ! [[ -v $op ]] && [ "${bashopts_optprop_setting[$op]}" == "true" ] && grep -E -q "^$op=" $bashopts_tool_settings_path; then
eval "tval=$(grep -E "^${op}=" $bashopts_tool_settings_path | sed -E "s/^[^=]+=('(.*)'|(.*))/\2\3/g"))"
fi
if [[ ! -v tval ]]; then
# set default value
tval=$dval
fi
if [ "${bashopts_optprop_interactive[$op]}" == "true" ] && [ "$bashopts_non_interactive" != "true" ]; then
echo "* ${bashopts_optprop_description[$op]}"
echo -n " [$tval]: "
read ival
if [ -n "$ival" ]; then
tval=$ival
fi
fi
eval "$op=$(declare -p tval | sed -E 's/^declare\s[^=]*=//g')"
fi
if [ "${bashopts_optprop_setting[$op]}" == "true" ]; then
# vrite the value to the setting file
sed -i "/^$op=/d" $bashopts_tool_settings_path
if [ "${!op}" != "$dval" ]; then
declare -p $op | sed -E 's/^declare\s-[-afFrxip ]*\s//g' >> $bashopts_tool_settings_path
fi
fi
done
if [ "$__bashopts_display_help__" == "true" ]; then
bashopts_diplay_help
exit 0
fi
}
......@@ -32,31 +32,46 @@ req_test_eq() {
}
_test_case_1() {
bashopts_setup "myapp_case1" "Test case 1"
bashopts_add -n interactive -l interactive -o i -d "Enable interactive mode" -t boolean -v true
bashopts_add -n config_path -l config -o c -d "Configuration path" -t string -v "/etc/myconf.cfg"
bashopts_diplay_help
}
_test_case_2() {
local dest1="This is a simple desc"
local dest2="This is a desc with a '\"' and a \$test"
bashopts_add -n toto -l auto -o t -d "$dest1"
bashopts_add -n tutu -l tutu -o u -d "$dest2"
req_test_eq "${bashopts_f_description[toto]}" "$dest1"
req_test_eq "${bashopts_f_description[tutu]}" "$dest2"
req_test_eq "${bashopts_optprop_description[toto]}" "$dest1"
req_test_eq "${bashopts_optprop_description[tutu]}" "$dest2"
}
_test_case_2() {
rm -f /tmp/myapp_case2rc
bashopts_setup -n "myapp_case3" -d "Test case 3" -s /tmp/myapp_case2rc
bashopts_add -n base_path -l base -o b -d "Base path" -t string -v "/opt/test" -i -s
bashopts_add -n config_path -l config -o c -d "Configuration path" -t string -e "\$base_path/myconf.cfg" -i -s
bashopts_parse_args --base /tmp -n
bashopts_process_args
req_test_eq "$config_path" "/tmp/myconf.cfg"
req_test_eq "$(cat /tmp/myapp_case2rc)" "base_path=\"/tmp\""
}
_test_case_3() {
bashopts_setup "myapp_case3" "Test case 3"
bashopts_add -n interactive -l interactive -o i -d "Enable interactive mode" -t boolean -v true
bashopts_add -n config_path -l config -o c -d "Configuration path" -t string -v "/etc/myconf.cfg"
bashopts_parse_args $@
rm -f /tmp/myapp_case3rc
bashopts_setup -n "myapp_case3" -d "Test case 3" -s /tmp/myapp_case3rc
bashopts_add -n process_file_list -l input-file -o f -d "Input file" -t string -m add -s
bashopts_parse_args --input-file toto --input-file=/tutu/titi
bashopts_process_args
set -x
req_test_eq "$(bashopts_join_by , ${process_file_list[@]})" "toto,/tutu/titi"
eval "test=$(grep -E "^process_file_list=" /tmp/myapp_case3rc | sed -E "s/^[^=]+=('(.*)'|(.*))/\2\3/g")"
req_test_eq "$(bashopts_join_by , ${test[@]})" "toto,/tutu/titi"
}
testno=$1
test_fn=$1
shift
_test_case_$testno $@
$test_fn $@
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