From 73300aaf18617e2a88bed1daf5da741fee29ef95 Mon Sep 17 00:00:00 2001 From: Denis Lehmann Date: Sun, 16 Jan 2022 15:37:35 +0100 Subject: [PATCH] add correct linebreaks to usage output --- README.org | 33 ++++++++++---- examples/add | 2 +- examples/greet | 22 ++++++--- sf | 119 ++++++++++++++++++++++++++++++++++++------------- 4 files changed, 129 insertions(+), 47 deletions(-) diff --git a/README.org b/README.org index e4c4ce7..c49fa32 100644 --- a/README.org +++ b/README.org @@ -23,7 +23,7 @@ #+begin_src sh # sf -- script framework (https://github.com/Deleh/sf) - sftrs=$'\e[0m';sftbf=$'\e[1m';sftdim=$'\e[2m';sftul=$'\e[4m';sftblk=$'\e[5m';sftinv=$'\e[7m';sfthd=$'\e[8m';sftclr=$'\e[1A\e[K';sftk=$'\e[30m';sftr=$'\e[31m';sftg=$'\e[32m';sfty=$'\e[33m';sftb=$'\e[34m';sftm=$'\e[35m';sftc=$'\e[36m';sftw=$'\e[97m';function sferr { echo "${sftbf}${sftr}ERROR${sftrs} $1";[ -z "$2" ]&&exit 1;};function sfwarn { echo "${sftbf}${sfty}WARNING${sftrs} $1";};function sfask { if [ -n "$2" ];then read -r -p "$1? [${sftbf}y${sftrs}/${sftbf}N${sftrs}] " sfin;[[ "$sfin" =~ n|N|^$ ]]&&sfin=false||sfin=true;else read -r -p "$1? [${sftbf}Y${sftrs}/${sftbf}n${sftrs}] " sfin;[[ "$sfin" =~ y|Y|^$ ]]&&sfin=true||sfin=false;fi;};function sfget { if [ -n "$2" ];then read -r -p "$1 [${sftbf}$2${sftrs}]: " sfin;else read -r -p "$1: " sfin;fi;[ "$sfin" == "" ]&&[ "$2" != "" ]&&sfin="$2";};function _sferr { echo "${sftbf}${sftr}SF PARSE ERROR${sftrs} $1";exit 1;};OLDIFS=$IFS;IFS=";";_sfphead="";_sfpdesc="";_sfodesc=" -h, --help;Show this help message\n";_sfexamples="";_sfpargs=();declare -A _sfflags;declare -A _sfargs;for a in "${sfargs[@]}";do _sfsubst=${a//";"};_sfcount="$(((${#a} - ${#_sfsubst})))";if [ $_sfcount -eq 1 ];then read -r -a _sfparsearr<<<"${a}";[[ " ${_sfpargs[*]} " =~ " ${_sfparsearr[0]} " ]]&&_sferr "'${_sfparsearr[0]}' is already set: $a";_sfpargs+=("${_sfparsearr[0]}");_sfphead="$_sfphead ${_sfparsearr[0]}";_sfpdesc="$_sfpdesc ${_sfparsearr[0]};${_sfparsearr[1]}\n";elif [ $_sfcount -eq 2 ];then read -r -a _sfparsearr<<<"${a}";[ -n "${_sfflags["--${_sfparsearr[0]}"]}" ]&&_sferr "'${_sfparsearr[0]}' is already set: $a";_sfflags["--${_sfparsearr[0]}"]="${_sfparsearr[0]}";[ -n "${_sfflags["-${_sfparsearr[1]}"]}" ]&&_sferr "'${_sfparsearr[1]}' is already set: $a";_sfflags["-${_sfparsearr[1]}"]="${_sfparsearr[0]}";declare "${_sfparsearr[0]}"=false;_sfodesc="$_sfodesc -${_sfparsearr[1]}, --${_sfparsearr[0]};${_sfparsearr[2]}\n";elif [ $_sfcount -eq 4 ];then read -r -a _sfparsearr<<<"${a}";[ -n "${_sfargs["--${_sfparsearr[0]}"]}" ]&&_sferr "'${_sfparsearr[0]}' is already set: $a";_sfargs["--${_sfparsearr[0]}"]="${_sfparsearr[0]}";[ -n "${_sfargs["-${_sfparsearr[1]}"]}" ]&&_sferr "'${_sfparsearr[1]}' is already set: $a";_sfargs["-${_sfparsearr[1]}"]="${_sfparsearr[0]}";declare "${_sfparsearr[0]}"="${_sfparsearr[3]}";_sfodesc="$_sfodesc -${_sfparsearr[1]}, --${_sfparsearr[0]} ${_sfparsearr[2]};${_sfparsearr[4]} (default: ${_sfparsearr[3]})\n";else _sferr "Wrong argument declaration: $a";fi;done;[ "$sfparr" == true ]&&[ "${#_sfpargs[@]}" == 0 ]&&_sferr "At least one positional argument must be used with 'sfparr'";for e in "${sfexamples[@]}";do _sfsubst=${e//";"};_sfcount="$(((${#e} - ${#_sfsubst})))";if [ $_sfcount -eq 1 ];then read -r -a _sfparsearr<<<"${e}";_sfexamples="$_sfexamples ${_sfparsearr[0]};${_sfparsearr[1]}\n";else _sferr "Wrong example declaration: $e";fi;done;IFS=$OLDIFS;function _sfusage { echo -n "Usage: $(basename "$0") [OPTIONS]";echo -ne "$_sfphead";[ "$sfparr" == true ]&&echo -n " ...";echo;[ -n "${sfdesc}" ]&&echo -e "\n$sfdesc";if [ "$_sfpdesc" != "" ];then echo -e "\nPOSITIONAL ARGUMENTS";echo -e "$_sfpdesc"|column -c 80 -s ";" -t -W 2;fi;if [ "$_sfodesc" != "" ];then echo -e "\nOPTIONS";echo -e "$_sfodesc"|column -c 80 -s ";" -t -W 2;fi;if [ "$_sfexamples" != "" ];then echo -e "\nEXAMPLES";echo -e "$_sfexamples"|column -c 80 -s ";" -t -W 2;fi;if [ -n "${sfextra}" ];then echo -e "\n$sfextra";fi;exit 0;};for a in "$@";do [ "$a" == "-h" ]||[ "$a" == "--help" ]&&_sfusage;done;while(("$#"));do if [ -n "${_sfflags["$1"]}" ];then declare "${_sfflags["$1"]}"=true;elif [ -n "${_sfargs["$1"]}" ];then if [ -n "$2" ]&&[ "${2:0:1}" != "-" ];then declare "${_sfargs["$1"]}"="$2";shift;else sferr "Argument for '$1' missing";fi;else if [ "${1:0:1}" == "-" ];then sferr "Unsupported argument: $1";else if [ "${#_sfpargs[@]}" != 0 ];then declare "${_sfpargs[0]}"="$1";[ "$sfparr" == true ]&&_sfplast="${_sfpargs[0]}"&&_sfparr=("$1");_sfpargs=("${_sfpargs[@]:1}");elif [ "$sfparr" == true ];then _sfparr+=("$1");else sferr "Too many positional arguments";fi;fi;fi;shift;done;[ "$sfparr" == true ]&&[ "${#_sfparr[@]}" -ge 1 ]&&read -r -a "${_sfplast?}"<<<"${_sfparr[@]}";if [ "$sfparr" != true ]&&[ "${#_sfpargs[@]}" -ge 1 ];then for p in "${_sfpargs[@]}";do sferr "Positional argument '$p' missing" 0;done;exit 1;fi;unset a e _sfargs _sferr _sfexamples _sfflags _sfodesc _sfpargs _sfparr _sfpdesc _sfphead _sfplast _sfusage + sftrs=$'\e[0m';sftbf=$'\e[1m';sftdim=$'\e[2m';sftul=$'\e[4m';sftblk=$'\e[5m';sftinv=$'\e[7m';sfthd=$'\e[8m';sftclr=$'\e[1A\e[K';sftk=$'\e[30m';sftr=$'\e[31m';sftg=$'\e[32m';sfty=$'\e[33m';sftb=$'\e[34m';sftm=$'\e[35m';sftc=$'\e[36m';sftw=$'\e[97m';function sferr { echo "${sftbf}${sftr}ERROR${sftrs} $1";[ -z "$2" ]&&exit 1;};function sfwarn { echo "${sftbf}${sfty}WARNING${sftrs} $1";};function sfask { if [ -n "$2" ];then read -r -p "$1? [${sftbf}y${sftrs}/${sftbf}N${sftrs}] " sfin;[[ "$sfin" =~ n|N|^$ ]]&&sfin=false||sfin=true;else read -r -p "$1? [${sftbf}Y${sftrs}/${sftbf}n${sftrs}] " sfin;[[ "$sfin" =~ y|Y|^$ ]]&&sfin=true||sfin=false;fi;};function sfget { if [ -n "$2" ];then read -r -p "$1 [${sftbf}$2${sftrs}]: " sfin;else read -r -p "$1: " sfin;fi;[ "$sfin" == "" ]&&[ "$2" != "" ]&&sfin="$2";};function _sferr { echo "${sftbf}${sftr}SF PARSE ERROR${sftrs} $1";exit 1;};OLDIFS=$IFS;IFS=";";_sfpargs=();_sfpheads=();_sfpoffset=0;_sfptails=();_sfpusage="";_sfoheads=();_sfooffset=0;_sfotails=();declare -A _sfflags;declare -A _sfargs;for a in "${sfargs[@]}";do _sfsubst=${a//";"};_sfcount="$(((${#a} - ${#_sfsubst})))";if [ "$_sfcount" -eq 1 ];then read -r -a _sfparsearr<<<"${a}";[[ " ${_sfpargs[*]} " =~ " ${_sfparsearr[0]} " ]]&&_sferr "'${_sfparsearr[0]}' is already set: $a";_sfpargs+=("${_sfparsearr[0]}");_sfpusage="$_sfpusage ${_sfparsearr[0]}";_sfphead="${_sfparsearr[0]}";[ "${#_sfphead}" -gt "${_sfpoffset}" ]&&_sfpoffset="${#_sfphead}";_sfpheads+=("$_sfphead");_sfptails+=("${_sfparsearr[1]}");elif [ "$_sfcount" -eq 2 ];then read -r -a _sfparsearr<<<"${a}";[ -n "${_sfflags["--${_sfparsearr[0]}"]}" ]&&_sferr "'${_sfparsearr[0]}' is already set: $a";_sfflags["--${_sfparsearr[0]}"]="${_sfparsearr[0]}";[ -n "${_sfflags["-${_sfparsearr[1]}"]}" ]&&_sferr "'${_sfparsearr[1]}' is already set: $a";_sfflags["-${_sfparsearr[1]}"]="${_sfparsearr[0]}";declare "${_sfparsearr[0]//-/_}"=false;_sfohead="-${_sfparsearr[1]}, --${_sfparsearr[0]}";[ "${#_sfohead}" -gt "${_sfooffset}" ]&&_sfooffset="${#_sfohead}";_sfoheads+=("$_sfohead");_sfotails+=("${_sfparsearr[2]}");elif [ "$_sfcount" -eq 4 ];then read -r -a _sfparsearr<<<"${a}";[ -n "${_sfargs["--${_sfparsearr[0]}"]}" ]&&_sferr "'${_sfparsearr[0]}' is already set: $a";_sfargs["--${_sfparsearr[0]}"]="${_sfparsearr[0]}";[ -n "${_sfargs["-${_sfparsearr[1]}"]}" ]&&_sferr "'${_sfparsearr[1]}' is already set: $a";_sfargs["-${_sfparsearr[1]}"]="${_sfparsearr[0]}";declare "${_sfparsearr[0]//-/_}"="${_sfparsearr[3]}";_sfohead="-${_sfparsearr[1]}, --${_sfparsearr[0]} ${_sfparsearr[2]}";[ "${#_sfohead}" -gt "${_sfooffset}" ]&&_sfooffset="${#_sfohead}";_sfoheads+=("$_sfohead");_sfotails+=("${_sfparsearr[4]} (default: ${_sfparsearr[3]})");else _sferr "Wrong argument declaration: $a";fi;done;_sfwidth=$(stty size|cut -d ' ' -f 2);_sfpoffset=$(("_sfpoffset" + 3));_sfpdesc="";for i in "${!_sfptails[@]}";do _sfptail="${_sfptails[$i]}";if [ $(("${#_sfptail}" + "$_sfpoffset"))-gt "$_sfwidth" ];then _sfptail=$(echo "$_sfptail"|fold -s -w "$((_sfwidth - _sfpoffset))");_sfptail="${_sfptail//$' \n'/$'\n;'}";fi;_sfpdesc="${_sfpdesc} ${_sfpheads[$i]};${_sfptail}\n";done;_sfooffset=$(("_sfooffset" + 3));_sfodesc=" -h, --help;Show this help message\n";for i in "${!_sfotails[@]}";do _sfotail="${_sfotails[$i]}";if [ $(("${#_sfotail}" + "$_sfooffset"))-gt "$_sfwidth" ];then _sfotail=$(echo "$_sfotail"|fold -s -w "$((_sfwidth - _sfooffset))");_sfotail="${_sfotail//$' \n'/$'\n;'}";fi;_sfodesc="${_sfodesc} ${_sfoheads[$i]};${_sfotail}\n";done;[ "$sfparr" == true ]&&[ "${#_sfpargs[@]}" == 0 ]&&_sferr "At least one positional argument must be used with 'sfparr'";_sfeheads=();_sfetails=();_sfeoffset=0;for e in "${sfexamples[@]}";do _sfsubst=${e//";"};_sfcount="$(((${#e} - ${#_sfsubst})))";if [ "$_sfcount" -eq 1 ];then read -r -a _sfparsearr<<<"${e}";_sfehead="${_sfparsearr[0]}";[ "${#_sfehead}" -gt "${_sfeoffset}" ]&&_sfeoffset="${#_sfehead}";_sfeheads+=("$_sfehead");_sfetails+=("${_sfparsearr[1]}");else _sferr "Wrong example declaration: $e";fi;done;_sfeoffset=$(("_sfeoffset" + 3));_sfexamples="";for i in "${!_sfetails[@]}";do _sfetail="${_sfetails[$i]}";if [ $(("${#_sfetail}" + "$_sfeoffset"))-gt "$_sfwidth" ];then _sfetail=$(echo "$_sfetail"|fold -s -w "$((_sfwidth - _sfeoffset))");_sfetail="${_sfetail//$' \n'/$'\n;'}";fi;_sfexamples="${_sfexamples} ${_sfeheads[$i]};${_sfetail}\n";done;IFS=$OLDIFS;function _sfusage { echo -n "Usage: $(basename "$0") [OPTIONS]";echo -ne "$_sfpusage";[ "$sfparr" == true ]&&echo -n " ...";echo;[ -n "${sfdesc}" ]&&echo -e "\n$sfdesc";if [ "$_sfpdesc" != "" ];then echo -e "\nPOSITIONAL ARGUMENTS";echo -e "$_sfpdesc"|column -s ";" -t -W 2;fi;if [ "$_sfodesc" != "" ];then echo -e "\nOPTIONS";echo -e "$_sfodesc"|column -s ";" -t -W 2;fi;if [ "$_sfexamples" != "" ];then echo -e "\nEXAMPLES";echo -e "$_sfexamples"|column -s ";" -t -W 2;fi;if [ -n "${sfextra}" ];then echo -e "\n$sfextra";fi;exit 0;};for a in "$@";do [ "$a" == "-h" ]||[ "$a" == "--help" ]&&_sfusage;done;while(("$#"));do if [ -n "${_sfflags["$1"]}" ];then declare "${_sfflags["$1"]//-/_}"=true;elif [ -n "${_sfargs["$1"]}" ];then if [ -n "$2" ]&&[ "${2:0:1}" != "-" ];then declare "${_sfargs["$1"]//-/_}"="$2";shift;else sferr "Argument for '$1' missing";fi;else if [ "${1:0:1}" == "-" ];then sferr "Unsupported argument/flag: $1";else if [ "${#_sfpargs[@]}" != 0 ];then declare "${_sfpargs[0]//-/_}"="$1";[ "$sfparr" == true ]&&_sfplast="${_sfpargs[0]//-/_}"&&_sfparr=("$1");_sfpargs=("${_sfpargs[@]:1}");elif [ "$sfparr" == true ];then _sfparr+=("$1");else sferr "Too many positional arguments";fi;fi;fi;shift;done;[ "$sfparr" == true ]&&[ "${#_sfparr[@]}" -gt 0 ]&&read -r -a "${_sfplast?}"<<<"${_sfparr[@]}";if [ "${#_sfpargs[@]}" -gt 0 ];then for p in "${_sfpargs[@]}";do sferr "Positional argument '$p' missing" 0;done;exit 1;fi;unset a e i _sfargs _sfehead _sfeheads _sfeoffset _sferr _sfetails _sfexamples _sfflags _sfodesc _sfohead _sfoheads _sfooffset _sfotails _sfpargs _sfparr _sfpdesc _sfphead _sfpheads _sfplast _sfpoffset _sfptails _sfpusage _sfusage _sfwidth #+end_src ** Requirements @@ -115,6 +115,8 @@ Then the variable =$verbose= exists with a value of either =false= or =true=. + *Note* that dashes in declared =sfargs= variable names get replaced with underscores. + **** Input functions User input can be requested with two functions. @@ -337,6 +339,9 @@ # Declare sf variables sfdesc="Greet a person." + sfargs+=("pretty-useless-flag;p;This is a pretty useless flag which is only used to show correct linebreaks of the usage. Change your terminal size and let this print again to see how the output adapts to your window") + sfargs+=("ask-for-lastname;l;Ask for lastname") + # Include sf, this could be replaced with a long oneliner source "$(dirname $0)/sf" @@ -344,14 +349,19 @@ # Actual script # ---------------------- - sfget "Enter your name" # Get input - echo "Hello ${sfin}!" # Use input + sfget "Enter your name" # Get input + echo "Hello ${sfin}!" # Use input - sfask "Do you want to tell me your age" # Ask for YES/no - if [ "$sfin" == true ]; then # Use answer - sfget "Enter your Age" "80" # Get input with default value - sfask "Is $sfin really your age" "no" # Use input and ask for yes/NO - if [ "$sfin" == true ]; then # Use answer + if [ "$ask_for_lastname" == true ]; then # Use variable with underscores instead of dashes + sfget "Enter your lastname" # Get input + echo "Ah I see, your lastname is ${sfin}" # Use input + fi + + sfask "Do you want to tell me your age" # Ask for YES/no + if [ "$sfin" == true ]; then # Use answer + sfget "Enter your Age" "80" # Get input with default value + sfask "Is $sfin really your age" "no" # Use input and ask for yes/NO + if [ "$sfin" == true ]; then # Use answer echo "Great!" else echo "I knew it!" @@ -367,7 +377,12 @@ Greet a person. OPTIONS - -h, --help Show this help message + -h, --help Show this help message + -p, --pretty-useless-flag This is a pretty useless flag which is only used to + show correct linebreaks of the usage. Change your + terminal size and let this print again to see how + the output adapts to your window + -l, --ask-for-lastname Ask for lastname #+end_example An example call looks like this: diff --git a/examples/add b/examples/add index 3d24d9c..ad860bc 100755 --- a/examples/add +++ b/examples/add @@ -31,4 +31,4 @@ for n in "${NUMBERS[@]}"; do # Use parsed positional argument array fi done -echo "The sum is: $sftbf$sum$sftrs" # Use text formatting variables \ No newline at end of file +echo "The sum is: $sftbf$sum$sftrs" # Use text formatting variables diff --git a/examples/greet b/examples/greet index 119b1c4..91ebf37 100755 --- a/examples/greet +++ b/examples/greet @@ -7,6 +7,9 @@ # Declare sf variables sfdesc="Greet a person." +sfargs+=("pretty-useless-flag;p;This is a pretty useless flag which is only used to show correct linebreaks of the usage. Change your terminal size and let this print again to see how the output adapts to your window") +sfargs+=("ask-for-lastname;l;Ask for lastname") + # Include sf, this could be replaced with a long oneliner source "$(dirname $0)/../sf" @@ -14,14 +17,19 @@ source "$(dirname $0)/../sf" # Actual script # ---------------------- -sfget "Enter your name" # Get input -echo "Hello ${sfin}!" # Use input +sfget "Enter your name" # Get input +echo "Hello ${sfin}!" # Use input -sfask "Do you want to tell me your age" # Ask for YES/no -if [ "$sfin" == true ]; then # Use answer - sfget "Enter your Age" "80" # Get input with default value - sfask "Is $sfin really your age" "no" # Use input and ask for yes/NO - if [ "$sfin" == true ]; then # Use answer +if [ "$ask_for_lastname" == true ]; then # Use variable with underscores instead of dashes + sfget "Enter your lastname" # Get input + echo "Ah I see, your lastname is ${sfin}" # Use input +fi + +sfask "Do you want to tell me your age" # Ask for YES/no +if [ "$sfin" == true ]; then # Use answer + sfget "Enter your Age" "80" # Get input with default value + sfask "Is $sfin really your age" "no" # Use input and ask for yes/NO + if [ "$sfin" == true ]; then # Use answer echo "Great!" else echo "I knew it!" diff --git a/sf b/sf index e42cb65..efca931 100644 --- a/sf +++ b/sf @@ -59,11 +59,14 @@ function _sferr { # Declare variables for parsing OLDIFS=$IFS IFS=";" -_sfphead="" -_sfpdesc="" -_sfodesc=" -h, --help;Show this help message\n" -_sfexamples="" _sfpargs=() +_sfpheads=() +_sfpoffset=0 +_sfptails=() +_sfpusage="" +_sfoheads=() +_sfooffset=0 +_sfotails=() declare -A _sfflags declare -A _sfargs @@ -74,7 +77,7 @@ for a in "${sfargs[@]}"; do _sfsubst=${a//";"} _sfcount="$(((${#a} - ${#_sfsubst})))" - if [ $_sfcount -eq 1 ]; then + if [ "$_sfcount" -eq 1 ]; then # Read positional argument declaration read -r -a _sfparsearr <<< "${a}" @@ -83,12 +86,15 @@ for a in "${sfargs[@]}"; do [[ " ${_sfpargs[*]} " =~ " ${_sfparsearr[0]} " ]] && _sferr "'${_sfparsearr[0]}' is already set: $a" _sfpargs+=("${_sfparsearr[0]}") - # Set usage header and description - _sfphead="$_sfphead ${_sfparsearr[0]}" - _sfpdesc="$_sfpdesc ${_sfparsearr[0]};${_sfparsearr[1]}\n" + # Set usage header and append description arrays + _sfpusage="$_sfpusage ${_sfparsearr[0]}" + _sfphead="${_sfparsearr[0]}" + [ "${#_sfphead}" -gt "${_sfpoffset}" ] && _sfpoffset="${#_sfphead}" + _sfpheads+=("$_sfphead") + _sfptails+=("${_sfparsearr[1]}") # Flags - elif [ $_sfcount -eq 2 ]; then + elif [ "$_sfcount" -eq 2 ]; then # Read flag declaration read -r -a _sfparsearr <<< "${a}" @@ -100,13 +106,16 @@ for a in "${sfargs[@]}"; do _sfflags["-${_sfparsearr[1]}"]="${_sfparsearr[0]}" # Set default value - declare "${_sfparsearr[0]}"=false + declare "${_sfparsearr[0]//-/_}"=false - # Set description - _sfodesc="$_sfodesc -${_sfparsearr[1]}, --${_sfparsearr[0]};${_sfparsearr[2]}\n" + # Append description arrays + _sfohead="-${_sfparsearr[1]}, --${_sfparsearr[0]}" + [ "${#_sfohead}" -gt "${_sfooffset}" ] && _sfooffset="${#_sfohead}" + _sfoheads+=("$_sfohead") + _sfotails+=("${_sfparsearr[2]}") # Arguments - elif [ $_sfcount -eq 4 ]; then + elif [ "$_sfcount" -eq 4 ]; then # Read argument declaration read -r -a _sfparsearr <<< "${a}" @@ -118,57 +127,107 @@ for a in "${sfargs[@]}"; do _sfargs["-${_sfparsearr[1]}"]="${_sfparsearr[0]}" # Set default value - declare "${_sfparsearr[0]}"="${_sfparsearr[3]}" + declare "${_sfparsearr[0]//-/_}"="${_sfparsearr[3]}" - # Set description - _sfodesc="$_sfodesc -${_sfparsearr[1]}, --${_sfparsearr[0]} ${_sfparsearr[2]};${_sfparsearr[4]} (default: ${_sfparsearr[3]})\n" + # Append description arrays + _sfohead="-${_sfparsearr[1]}, --${_sfparsearr[0]} ${_sfparsearr[2]}" + [ "${#_sfohead}" -gt "${_sfooffset}" ] && _sfooffset="${#_sfohead}" + _sfoheads+=("$_sfohead") + _sfotails+=("${_sfparsearr[4]} (default: ${_sfparsearr[3]})") else _sferr "Wrong argument declaration: $a" fi done +# Set _sfwidth to current terminal size +_sfwidth=$(stty size | cut -d ' ' -f 2) + +# Create positional argument description with correct line breaks +_sfpoffset=$(( "_sfpoffset" + 3 )) +_sfpdesc="" +for i in "${!_sfptails[@]}"; do + _sfptail="${_sfptails[$i]}" + if [ $(( "${#_sfptail}" + "$_sfpoffset" )) -gt "$_sfwidth" ]; then + _sfptail=$(echo "$_sfptail" | fold -s -w "$(( _sfwidth - _sfpoffset ))") + _sfptail="${_sfptail//$' \n'/$'\n;'}" + fi + _sfpdesc="${_sfpdesc} ${_sfpheads[$i]};${_sfptail}\n" +done + +# Create option description with correct line breaks +_sfooffset=$(( "_sfooffset" + 3 )) +_sfodesc=" -h, --help;Show this help message\n" +for i in "${!_sfotails[@]}"; do + _sfotail="${_sfotails[$i]}" + if [ $(( "${#_sfotail}" + "$_sfooffset" )) -gt "$_sfwidth" ]; then + _sfotail=$(echo "$_sfotail" | fold -s -w "$(( _sfwidth - _sfooffset ))") + _sfotail="${_sfotail//$' \n'/$'\n;'}" + fi + _sfodesc="${_sfodesc} ${_sfoheads[$i]};${_sfotail}\n" +done + # Check if at least one positional argument is set if 'sfparr' is used [ "$sfparr" == true ] && [ "${#_sfpargs[@]}" == 0 ] && _sferr "At least one positional argument must be used with 'sfparr'" # Parse examples +_sfeheads=() +_sfetails=() +_sfeoffset=0 for e in "${sfexamples[@]}"; do # Get amount of ; _sfsubst=${e//";"} _sfcount="$(((${#e} - ${#_sfsubst})))" - if [ $_sfcount -eq 1 ]; then + if [ "$_sfcount" -eq 1 ]; then # Read example read -r -a _sfparsearr <<< "${e}" - _sfexamples="$_sfexamples ${_sfparsearr[0]};${_sfparsearr[1]}\n" + # Append example arrays + _sfehead="${_sfparsearr[0]}" + [ "${#_sfehead}" -gt "${_sfeoffset}" ] && _sfeoffset="${#_sfehead}" + _sfeheads+=("$_sfehead") + _sfetails+=("${_sfparsearr[1]}") + else _sferr "Wrong example declaration: $e" fi done +# Create examples description with correct line breaks +_sfeoffset=$(( "_sfeoffset" + 3 )) +_sfexamples="" +for i in "${!_sfetails[@]}"; do + _sfetail="${_sfetails[$i]}" + if [ $(( "${#_sfetail}" + "$_sfeoffset" )) -gt "$_sfwidth" ]; then + _sfetail=$(echo "$_sfetail" | fold -s -w "$(( _sfwidth - _sfeoffset ))") + _sfetail="${_sfetail//$' \n'/$'\n;'}" + fi + _sfexamples="${_sfexamples} ${_sfeheads[$i]};${_sfetail}\n" +done + IFS=$OLDIFS # Usage function function _sfusage { echo -n "Usage: $(basename "$0") [OPTIONS]" - echo -ne "$_sfphead" + echo -ne "$_sfpusage" [ "$sfparr" == true ] && echo -n " ..." echo [ -n "${sfdesc}" ] && echo -e "\n$sfdesc" if [ "$_sfpdesc" != "" ]; then echo -e "\nPOSITIONAL ARGUMENTS" - echo -e "$_sfpdesc" | column -c 80 -s ";" -t -W 2 + echo -e "$_sfpdesc" | column -s ";" -t -W 2 fi if [ "$_sfodesc" != "" ]; then echo -e "\nOPTIONS" - echo -e "$_sfodesc" | column -c 80 -s ";" -t -W 2 + echo -e "$_sfodesc" | column -s ";" -t -W 2 fi if [ "$_sfexamples" != "" ]; then echo -e "\nEXAMPLES" - echo -e "$_sfexamples" | column -c 80 -s ";" -t -W 2 + echo -e "$_sfexamples" | column -s ";" -t -W 2 fi if [ -n "${sfextra}" ]; then echo -e "\n$sfextra" @@ -187,14 +246,14 @@ while (( "$#" )); do # Check if flag if [ -n "${_sfflags["$1"]}" ]; then - declare "${_sfflags["$1"]}"=true + declare "${_sfflags["$1"]//-/_}"=true # Check if argument elif [ -n "${_sfargs["$1"]}" ]; then # Check if argument has value if [ -n "$2" ] && [ "${2:0:1}" != "-" ]; then - declare "${_sfargs["$1"]}"="$2" + declare "${_sfargs["$1"]//-/_}"="$2" shift else sferr "Argument for '$1' missing" @@ -204,12 +263,12 @@ while (( "$#" )); do else # Check if arg starts with - if [ "${1:0:1}" == "-" ]; then - sferr "Unsupported argument: $1" + sferr "Unsupported argument/flag: $1" else # Set positional argument if [ "${#_sfpargs[@]}" != 0 ]; then - declare "${_sfpargs[0]}"="$1" - [ "$sfparr" == true ] && _sfplast="${_sfpargs[0]}" && _sfparr=("$1") + declare "${_sfpargs[0]//-/_}"="$1" + [ "$sfparr" == true ] && _sfplast="${_sfpargs[0]//-/_}" && _sfparr=("$1") _sfpargs=("${_sfpargs[@]:1}") elif [ "$sfparr" == true ]; then _sfparr+=("$1") @@ -222,10 +281,10 @@ while (( "$#" )); do done # Parse additional arguments if 'sfparr' is set -[ "$sfparr" == true ] && [ "${#_sfparr[@]}" -ge 1 ] && read -r -a "${_sfplast?}" <<< "${_sfparr[@]}" +[ "$sfparr" == true ] && [ "${#_sfparr[@]}" -gt 0 ] && read -r -a "${_sfplast?}" <<< "${_sfparr[@]}" # Check if positional arguments left -if [ "$sfparr" != true ] && [ "${#_sfpargs[@]}" -ge 1 ]; then +if [ "${#_sfpargs[@]}" -gt 0 ]; then for p in "${_sfpargs[@]}"; do sferr "Positional argument '$p' missing" 0 done @@ -233,4 +292,4 @@ if [ "$sfparr" != true ] && [ "${#_sfpargs[@]}" -ge 1 ]; then fi # Unset all internal variables and functions -unset a e _sfargs _sferr _sfexamples _sfflags _sfodesc _sfpargs _sfparr _sfpdesc _sfphead _sfplast _sfusage +unset a e i _sfargs _sfehead _sfeheads _sfeoffset _sferr _sfetails _sfexamples _sfflags _sfodesc _sfohead _sfoheads _sfooffset _sfotails _sfpargs _sfparr _sfpdesc _sfphead _sfpheads _sfplast _sfpoffset _sfptails _sfpusage _sfusage _sfwidth