PRbL/functions
2023-05-09 11:47:17 -07:00

1018 lines
27 KiB
Bash
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# pyr0ball script functions library
# Initial Vars
functionsrev=1.1.15
#scriptname="${BASH_SOURCE[0]##*/}"
#rundir="${BASH_SOURCE[0]%/*}"
#rundir_absolute=$(cd $rundir && pwd)
#runuser="$(whoami)"
pretty_date="$(date +%Y-%m-%d_%H-%M-%S)"
short_date="$(date +%Y-%m-%d)"
# return any cursor mods to normal on exit
cleanup(){
tput cnorm
}
trap cleanup EXIT
# Escape characters (if your shell uses a different one, you can modify it here)
# By default this is using the usual bash escape code
ESC=$( printf '\033')
# Detect OS type
case $OSTYPE in
linux-gnu* ) ESC=$( printf '\033') ;;
darwin* ) ESC=$( printf '\e') ;;
cygwin ) ESC=$( printf '\033') ;;
msys ) ESC=$( printf '\033') ;;
esac
# Utilities (Setting up colors and bounding boxes)
# boxtype changes what borders are used for the boxborder functions.
# options are "single" "double" "char"
# for the "char" option, change the "borderchar" variable to
# whichever unicode character you wish to use
# boxtype="${boxtype:-single}" # Sets default 'single' if not already set
# borderchar="${borderchar:-#}" # Sets default '#' if not already set
boxtype="norm" # Sets default 'single' if not already set
borderchar="#" # Sets default '#' if not already set
# Colorization options
if [[ "$TERM" != "linux" ]] ; then
red="${ESC}[38;5;1m" # red
grn="${ESC}[38;5;2m" # green
ylw="${ESC}[38;5;3m" # yellow
blu="${ESC}[38;5;27m" # blue
lbl="${ESC}[38;5;69m" # light blue
mag="${ESC}[38;5;5m" # magenta
cyn="${ESC}[38;5;6m" # cyan
pur="${ESC}[38;5;135m" # purple
ong="${ESC}[38;5;166m" # orange
lyl="${ESC}[38;5;228m" # light yellow
lrd="${ESC}[38;5;196m" # light red
gry="${ESC}[38;5;240m" # Grey
norm=$"${ESC}[39m" # default/normal
#
bld="${ESC}[1m" # bold
unb="${ESC}[21m" # un-bold
dim="${ESC}[2m" # dim
und="${ESC}[22m" # un-dim
unl="${ESC}[4m" # underline
nln="${ESC}[24" # not-underline
blk="${ESC}[5" # blinking
unbl="${ESC}[25" # stop blinking
inv="${ESC}[7m" # invert
rsinv="${ESC}[27" # reset http://www.endmemo.com/unicode/unicodeconverter.phpnvert
hid="${ESC}[8" # hidden
unh="${ESC}[28" # unhide
dfl="${ESC}[0m" # restore default
fi
# Extra Unicode Character Manipulation
case $OSTYPE in
linux-gnu*|cygwin|msys)
return_arrow=$(echo -e "\u2BAC")
enter_arrow=$(echo -e "\u21B5")
green_check=${grn}$(echo -e "\u2714")${dfl}
red_x=${lrd}$(echo -e "\u00D7")${dfl}
selected_opt=$(echo -e "\u25C9")
deselected_opt=$(echo -e "\u25CE")
;;&
darwin* )
return_arrow=$(echo -e "⮬")
enter_arrow=$(echo -e "↵")
green_check=${grn}$(echo -e "✔")${dfl}
red_x=${lrd}$(echo -e "×")${dfl}
selected_opt=$(echo -e "◉")
deselected_opt=$(echo -e "◎")
;;
esac
# For drawing pretty boxes
terminal_width=$(tput cols)
if [ $terminal_width -le 81 ] ; then
BOXWIDTH=$((terminal_width - 1))
else
BOXWIDTH=80
fi
# Box Drawing characters
case $OSTYPE in
linux-gnu*|cygwin|msys)
light_h=$(echo -e "\u2500")
norm_h=$(echo -e "\u2501")
double_h=$(echo -e "\u2550")
light_v=$(echo -e "\u2502")
norm_v=$(echo -e "\u2503")
double_v=$(echo -e "\u2551")
;;&
darwin* )
light_h=$(echo -e "─")
norm_h=$(echo -e "━")
double_h=$(echo -e "═")
light_v=$(echo -e "│")
norm_v=$(echo -e "┃")
double_v=$(echo -e "║")
;;
esac
# Box border type single-line
box-norm(){
# ---------------------------------------#
case $OSTYPE in
linux-gnu*|cygwin|msys)
top_border=${norm_h}
bottom_border=${norm_h}
left_border=${norm_v}
right_border=${norm_v}
left_top_border=$(echo -e "\u250f")
right_top_border=$(echo -e "\u2513")
left_bottom_border=$(echo -e "\u2517")
right_bottom_border=$(echo -e "\u251b")
box_break_line=$(echo -e "\u25AB")
;;&
darwin* )
top_border=${norm_h}
bottom_border=${norm_h}
left_border=${norm_v}
right_border=${norm_v}
left_top_border=$(echo -e "┏")
right_top_border=$(echo -e "┓")
left_bottom_border=$(echo -e "┗")
right_bottom_border=$(echo -e "┛")
box_break_line=$(echo -e "▫")
;;
esac
# ---------------------------------------#
}
# Box border type double-line
box-double(){
# ---------------------------------------#
case $OSTYPE in
linux-gnu*|cygwin|msys)
top_border=${double_h}
bottom_border=${double_h}
left_border=${double_v}
right_border=${double_v}
left_top_border=$(echo -e "\u2554")
right_top_border=$(echo -e "\u2557")
left_bottom_border=$(echo -e "\u255A")
right_bottom_border=$(echo -e "\u255D")
box_break_line=$(echo -e "\u25AB")
;;&
darwin* )
top_border=${double_h}
bottom_border=${double_h}
left_border=${double_v}
right_border=${double_v}
left_top_border=$(echo -e "╔")
right_top_border=$(echo -e "╗")
left_bottom_border=$(echo -e "╚")
right_bottom_border=$(echo -e "╝")
box_break_line=$(echo -e "▫")
;;
esac
# ---------------------------------------#
}
# Box border type thick-line
box-heavy(){
# ---------------------------------------#
case $OSTYPE in
linux-gnu*|cygwin|msys)
top_border=$(echo -e "\u2580")
bottom_border=$(echo -e "\u2584")
left_border=$(echo -e "\u258C")
right_border=$(echo -e "\u2590")
left_top_border=$(echo -e "\u259B")
right_top_border=$(echo -e "\u259C")
left_bottom_border=$(echo -e "\u2599")
right_bottom_border=$(echo -e "\u259F")
box_break_line=$(echo -e "\u25AC")
;;&
darwin* )
top_border=$(echo -e "▀")
bottom_border=$(echo -e "▄")
left_border=$(echo -e "▌")
right_border=$(echo -e "▐")
left_top_border=$(echo -e "▛")
right_top_border=$(echo -e "▜")
left_bottom_border=$(echo -e "▙")
right_bottom_border=$(echo -e "▟")
box_break_line=$(echo -e "▫")
;;
esac
# ---------------------------------------#
}
# Box border type thin-line
box-light(){
# ---------------------------------------#
#top_border=$(echo -e '\u23BA')
case $OSTYPE in
linux-gnu*|cygwin|msys)
top_border=${light_h}
bottom_border=${light_h}
left_border=${light_v}
right_border=${light_v}
left_top_border=$(echo -e "\u23BE")
right_top_border=$(echo -e "\u23CB")
left_bottom_border=$(echo -e "\u23BF")
right_bottom_border=$(echo -e "\u23CC")
box_break_line=$(echo -e "\u23AF")
;;&
darwin* )
top_border=${light_h}
bottom_border=${light_h}
left_border=${light_v}
right_border=${light_v}
left_top_border=$(echo -e "⎾")
right_top_border=$(echo -e "⏋")
left_bottom_border=$(echo -e "⎿")
right_bottom_border=$(echo -e "⏌")
box_break_line=$(echo -e "⎯")
;;
esac
# ---------------------------------------#
}
box-rounded(){
# ---------------------------------------#
case $OSTYPE in
linux-gnu*|cygwin|msys)
top_border=${light_h}
bottom_border=${light_h}
left_border=${light_v}
right_border=${light_v}
left_top_border=$(echo -e "\u256D")
right_top_border=$(echo -e "\u256E")
left_bottom_border=$(echo -e "\u2570")
right_bottom_border=$(echo -e "\u256F")
box_break_line=$(echo -e "\u25A2")
;;&
darwin* )
top_border=${light_h}
bottom_border=${light_h}
left_border=${light_v}
right_border=${light_v}
left_top_border=$(echo -e "╭")
right_top_border=$(echo -e "╮")
left_bottom_border=$(echo -e "╰")
right_bottom_border=$(echo -e "╯")
box_break_line=$(echo -e "▢")
;;
esac
# ---------------------------------------#
}
box-singlechar(){
# ---------------------------------------#
top_border=$borderchar
bottom_border=$borderchar
left_border=$borderchar
right_border=$borderchar
left_top_border=$borderchar
right_top_border=$borderchar
left_bottom_border=$borderchar
right_bottom_border=$borderchar
box_break_line="-"
# ---------------------------------------#
}
set-boxtype(){
case $boxtype in
norm) box-norm ;;
double) box-double ;;
heavy) box-heavy ;;
light) box-light ;;
rounded) box-rounded ;;
char) box-singlechar ;;
esac
}
#set-boxtype
repchar() {
n=1
while [ $n -le $2 ] ; do
echo -n "$1"
n=$((n+1))
done
}
boxtop() {
echo -n "$left_top_border"
repchar "$top_border" $((BOXWIDTH-1))
echo -n "$right_top_border"
echo
}
boxbottom() {
echo -n "$left_bottom_border"
repchar "$bottom_border" $((BOXWIDTH-1))
echo -n "$right_bottom_border"
echo
}
boxline() {
echo -e "$left_border $1\r${ESC}[${BOXWIDTH}C$right_border"
}
boxseparator(){
repchar "$box_break_line" $((BOXWIDTH-3))
echo
}
# # For printing center-justified text. To change the padding character, replace the ' ' <~~ whitespace in front of the '{' in the padding variable
# center() {
# padding="$(printf '%0.1s' {1..500})"
# printf '%*.*s %s %*.*s\n' 0 "$(((terminal_width-2-${#1})/2))" "$padding" "$1" 0 "$(((termwidth-1-${#1})/2))" "$padding"
# }
# For printing center-justified text. To change the padding character, replace the ' ' <~~ whitespace in front of the '{' in the padding variable
center(){
#local padding="";
padding="$(printf '%0.1s' \ {1..100})"
#padding=" "
printf '%*.*s %s %*.*s\n' 0 "$(((BOXWIDTH-2-${#1})/2))" "${left_border}${padding}" "$1" 0 "$(((BOXWIDTH-1-${#1})/2))" "${padding}${right_border}"
#printf "%s %s %s %s %s\n" "$left_border" "$padding" "$1" "$padding" "$right_border";
#echo -en "\r${ESC}[$(((BOXWIDTH-${#1})/2))C$@\r${ESC}[${BOXWIDTH}C\n"
}
# For printing spanned text, e.g. single-pair lists ($name...$title)
spanner() {
# 1: left-side-text, 2: right-side-text
# Spanner character:
spacer="."
local _spanner="";
eval printf -v _spanner \'"%0.1s"\' "$spacer"{1..$[$((BOXWIDTH-1))- 2 - ${#1} - ${#2}]}
printf "%s %s %s\n" "$1" "$_spanner" "$2";
}
boxborder(){
boxtop
for line in "$@" ; do
boxline "$line"
done
boxbottom
}
subboxborder(){
BOXWIDTH_ORG=$BOXWIDTH
BOXWIDTH=$(($BOXWIDTH*75/100))
boxline $(boxtop)
for line in "$@" ; do
boxline $(boxline "$line")
done
boxline $(boxbottom)
BOXWIDTH=$BOXWIDTH_ORG
}
success(){
_line="$@"
echo -e "\n"
boxborder "${scriptname} ${grn}SUCCESS${dfl} ${_line}"
exit 0
}
warn(){
ec=$?
_line="$@"
echo -e "\n"
boxtop
boxline "${lrd}WARNING${lyl}[${ong}code=${red}$ec${lyl}]: ${_line}${dfl}"
boxbottom
}
fail(){
ec=$?
_line="$@"
echo -e "\n"
boxtop
boxline "${lrd}FAILED${lyl}[${ong}code=${red}$ec${lyl}]: ${_line}${dfl}"
boxbottom
exit $ec
}
pushd(){
command pushd "$@" > /dev/null
}
popd(){
command popd "$@" > /dev/null
}
popdfail(){
ec=$?
popd
$(exit $ec) #restore the exit code
fail "$@"
}
run(){
_cmd=$@
if [[ $dry_run != true ]] ; then
$_cmd
else
boxline "DryRun: $_cmd"
fi
}
run-and-log(){
_cmd=$@
if [[ $dry_run != true ]] ; then
logger $_cmd
else
logger "DryRun: $_cmd"
fi
}
logger(){
if [ ! -f $logfile ] ; then
run mkdir -p ${rundir%/*}
touch $logfile
fi
$@ 2>&1 | tee >(
while IFS= read -r line; do
# strip any escaped strings for logging output
_line=$(echo $line | sed "s,\x1B\[[0-9;]*[a-zA-Z],,g" | tr -dc '[:print:]')
# only print out non-empty lines to log
if [[ "$_line" != "" ]] ; then
printf '[%s] %s\n' "${scriptname}][${pretty_date}" "$_line"
fi
done >> $logfile
)
}
# File handling functions
take-backup(){
name="$1"
if [[ $update_run != true ]] ; then
# Check if a backup file or symbolic link already exists
if [[ -e "$name.bak" || -L "$name.bak" ]]; then
run boxline " $name.bak backup already exists"
else
# Check if the file is a hidden file (starts with a dot)
if [[ "$name" == .* ]]; then
# Add a dot to the beginning of the backup file name
backup_name=".${name}.bak"
else
# Create the backup file name by appending ".bak" to the original file name
backup_name="${name}.bak"
fi
# Copy the file to the backup file with preservation of file attributes
run cp -p "$name" "$backup_name"
# Add the original file to the list of backup files
backup_files+=("$name")
# Log the original file name to the backup file list file
run echo "$name" >> "$rundir/backup_files.list"
fi
fi
}
restore-backup(){
run echo "${#backup_files[@]}"
for file in "${backup_files[@]}" ; do
run cp "$file".bak $file
run echo "$file is restored"
done
backup_files=()
if [ -f $rundir/backup_files.list ] ; then
run rm $rundir/backup_files.list
fi
}
install-file(){
local _source="$1"
local _destination="$2"
local _source_root="$3"
local _filename=${_source##*/}
local _destination_file=${_destination}/${_filename#${_source_root}}
installed_files+=("${_destination_file}")
if [[ $update_run == true ]] ; then
run boxline "$scriptname: added file ${_destination_file} to list"
else
run cp -p $_source $_destination_file && boxline "Installed ${_filename}" || warn "Unable to install ${_filename}"
run echo "${_destination_file}" >> $rundir/installed_files.list
fi
}
install-dir() {
local _source="$1"
local _destination="$2"
installed_dirs+=("$_source -> $_destination")
# Iterate through all files in the source directory recursively
while IFS= read -r -d '' source_file; do
# Construct the destination file path by removing the source directory path
# and appending it to the destination directory path
local _filename="${source_file#${_source}}"
local destination_file="${_destination}/${source_file#${_source}}"
# Create the parent directory of the destination file if it doesn't exist
# Log the destination file path to the logfile
#echo "$destination_file" >> "$logfile"
installed_files+=($destination_file)
if [[ $update_run == true ]] ; then
run boxline "$scriptname: added file ${destination_file} to list"
else
run mkdir -p "$(dirname "$destination_file")"
run cp -p ${_source}${_filename} $destination_file && boxline "Installed ${_filename}" || warn "Unable to install ${_filename}"
run echo "${destination_file}" >> $rundir/installed_files.list
fi
done < <(find "$_source" -type f -print0)
}
clone-repo(){
# git clone <source url> <destination>
local _url=$1
local _destination=$2
if [ ! -z $_destination ] ; then
run mkdir -p $_destination
fi
run git clone --recurse-submodules $_url $_destination
}
run-from-url(){
url=$1
args=${*:2}
# check the interpreter using the shebang at the top of the file
interpreter=$(curl -ks $url | head -n 1 | sed -e 's/\#\!//g' 2>/dev/null)
# In case of missing shebang, default to bash
if [ -z $interpreter ] ; then
interpreter="/bin/bash"
fi
logger boxline "Running online script $url with args $args"
if [[ "$interpreter" == "/bin/bash "]] ; then
run curl -H 'Cache-Control: no-cache' -ks $url | $interpreter -s -- $args
else
run curl -H 'Cache-Control: no-cache' -ks $url > ${url##*/}
run $interpreter ${url##*/} $args
ec=$?
run rm ${url##*/}
return $ec
}
ctrl_c(){
echo -e "\n"
fail "User interrupted with Ctrl-C"
}
prbl-logo(){
echo -e \
"
_!*(xL}}xvr!,.
>}KM3nLL}}}}}LLTh%5u>.
<FEnviT3Egg8888888DGnivnOKr.
.vEP?vZ88888QBB####BQ88888ZxrjNT.
!MW^LD888Q#@@@@######@@@#B888gn>P8}.
?g2!%888BBev*<!!!!!!!!!>***^!!!!!.LB&?
>gF=Rg88#5 ,^}#Be-
Pg'a888#B ,unnnnn}- .ZO_N#Bgr
-gZ-g888B, .U88!j#B#Qs.
.NM-D88N! .j888=XQQQOQQ=
28>u8g< ,!!!!!!!!!!!!!!!!>LpQ888K,QQgy ,V>
_OM:K? .&#Q88888888888888#@@#888M:p##Q8G! .
,WM!.!!^p#@@##Q88g8888QB#@@#Q888e!W#####BBg2!
.u&n^yg888Q##@@@@@@@@@@#B8888K*}Q###BQQQBB##gL.
_nDj?xPggg888QQQQQ888888Wx?zQ####BQB88&2vvnWQ(
.^yOjLLL}jP%OEOM5suLxLjQ#######Q#@Q8Z. r<
.!}%QQNWKessaG%gB###########BQ@@@#R?.
.!LP8B###Q5V%8B##B88QQB##BB#@@@@@Qe?,
_*TW#P_ !KQ#@BM^^}OB##BB#@@@@@@@Ba<
-<!. :jg##h: _ryW8BBBB##@@@@@#}.
'!?nn?=. .-_,=^xAQ#@@B>
>sQ@@?
_nB#,
-hv
"
}
# ensure ctrl-c to cancel script ends the entire process and not just the current function
# trap ctrl_c INT # Commented out by default to prevent abnormal background exits
# Function for spinner status
# usage: spin "command [args]"
set_spinner() {
case $1 in
spinner1)
FRAME="⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
FRAME_INTERVAL=0.05
;;
spinner2)
FRAME="-\|/"
FRAME_INTERVAL=0.1
;;
spinner3)
FRAME="◐◓◑◒"
FRAME_INTERVAL=0.2
;;
spinner4)
FRAME="⇐⇖⇑⇗⇒⇘⇓⇙"
FRAME_INTERVAL=0.1
;;
spinner5)
FRAME="◇◈◆◈"
FRAME_INTERVAL=0.2
;;
spinner6)
FRAME="⚬⚭⚮⚯⚮⚭"
FRAME_INTERVAL=0.15
;;
spinner7)
FRAME="░▒▓█▓▒"
FRAME_INTERVAL=0.2
;;
spinner8)
FRAME="☉◎◉◎☉"
FRAME_INTERVAL=0.1
;;
spinner9)
FRAME="♡♥❤♥♡"
FRAME_INTERVAL=0.15
;;
spinner10)
FRAME="▁▂▃▄▅▆▇█▇▆▅▄▃▂▁"
FRAME_INTERVAL=0.1
;;
spinner11)
FRAME="←↖↑↗→↘↓↙"
FRAME_INTERVAL=0.1
;;
spinner12)
FRAME="▖▘▝▗"
FRAME_INTERVAL=0.2
;;
spinner13)
FRAME="▙▛▜▟"
FRAME_INTERVAL=0.2
;;
spinner14)
FRAME="▚▞"
FRAME_INTERVAL=0.5
;;
spinner15)
FRAME="☰☱☳☶☵☳☶☴☰☰"
FRAME_INTERVAL=0.1
;;
spinner16)
FRAME="▤▧▥▨"
FRAME_INTERVAL=0.2
;;
spinner17)
FRAME="▉▊▋▌▍▎▏▎▍▌▋▊▉"
FRAME_INTERVAL=0.1
;;
spinner18)
FRAME="⣾⣽⣻⢿⡿⣟⣯⣷"
FRAME_INTERVAL=0.1
;;
spinner19)
FRAME="┤┘┴└├┌┬┐"
FRAME_INTERVAL=0.1
;;
*)
warn "No spinner is defined for $1"
esac
}
spin() {
CMD=$@
tput civis
sc=0
$CMD &
_PID=$!
while [ -d /proc/$_PID ] ; do
for i in "${FRAME[@]}" ; do
printf "\b${FRAME:sc++:1}"
((sc==${#FRAME})) && sc=0
#echo -ne "\b${FRAME[$i]}"
sleep $FRAME_INTERVAL
done
done
printf "\r\n"
tput cnorm
}
spinstart() {
sstop=false
tput civis
sc=0
while [[ $sstop == false ]] ; do
for i in "${FRAME[@]}" ; do
printf "\b${FRAME:sc++:1}"
((sc==${#FRAME})) && sc=0
#echo -ne "\b${FRAME[$i]}"
sleep $FRAME_INTERVAL
done
done
}
spinstop() {
export sstop=true
printf "\r\n"
tput cnorm
}
# https://github.com/fearside/ProgressBar/blob/master/progressbar.sh
progress-bar() {
# Process data
let _progress=(${1}*100/${2}*100)/100
let _done=(${_progress}*4)/10
let _left=40-$_done
# Build progressbar string lengths
_done=$(printf "%${_done}s")
_left=$(printf "%${_left}s")
# 1.2 Build progressbar strings and print the ProgressBar line
# 1.2.1 Output example:
# 1.2.1.1 Progress : [########################################] 100%
printf "\rProgress : [${_done// /#}${_left// /-}] ${_progress}%%"
}
# Renders a text based list of options that can be selected by the
# user using up, down and enter keys and returns the chosen option.
#
# Arguments : list of options, maximum of 256
# "opt1" "opt2" ...
# Return value: selected index (0 for opt1, 1 for opt2 ...)
select_option(){
# little helpers for terminal print control and key input
cursor_blink_on() { printf "$ESC[?25h"; }
cursor_blink_off() { printf "$ESC[?25l"; }
cursor_to() { printf "$ESC[$1;${2:-1}H"; }
print_option() { printf " $1 "; }
print_selected() { printf " $ESC[7m $1 $ESC[27m"; }
get_cursor_row() { IFS=';' read -sdR -p $'\E[6n' ROW COL; echo ${ROW#*[}; }
key_input() { read -s -n3 key 2>/dev/null >&2
if [[ $key = $ESC[A ]]; then echo up; fi
if [[ $key = $ESC[B ]]; then echo down; fi
if [[ $key = "" ]]; then echo enter; fi; }
# initially print empty new lines (scroll down if at bottom of screen)
for opt; do printf "\n"; done
# determine current screen position for overwriting the options
local lastrow=`get_cursor_row`
local startrow=$(($lastrow - $#))
# ensure cursor and input echoing back on upon a ctrl+c during read -s
trap "cursor_blink_on; stty echo; printf '\n'; exit" 2
cursor_blink_off
local selected=0
while true; do
# print options by overwriting the last lines
local idx=0
for opt; do
cursor_to $(($startrow + $idx))
if [ $idx -eq $selected ]; then
print_selected "$opt"
else
print_option "$opt"
fi
((idx++))
done
# user key control
case `key_input` in
enter) break;;
up) ((selected--));
if [ $selected -lt 0 ]; then selected=$(($# - 1)); fi;;
down) ((selected++));
if [ $selected -ge $# ]; then selected=0; fi;;
esac
done
# cursor position back to normal
cursor_to $lastrow
printf "\n"
cursor_blink_on
return $selected
}
# Example for above select_option
#echo "Select one option using up/down keys and enter to confirm:"
#echo
#options=("one" "two" "three")
#select_option "${options[@]}"
#choice=$?
#echo "Choosen index = $choice"
#echo " value = ${options[$choice]}"
select_opt(){
select_option "$@" 1>&2
local result=$?
echo $result
return $result
}
# Examples for above select_opt
#case `select_opt "Yes" "No" "Cancel"` in
# 0) echo "selected Yes";;
# 1) echo "selected No";;
# 2) echo "selected Cancel";;
#esac
#options=("Yes" "No" "${array[@]}") # join arrays to add some variable array
#case `select_opt "${options[@]}"` in
# 0) echo "selected Yes";;
# 1) echo "selected No";;
# *) echo "selected ${options[$?]}";;
#esac
multiselect(){
# little helpers for terminal print control and key input
cursor_blink_on() { printf "$ESC[?25h"; }
cursor_blink_off() { printf "$ESC[?25l"; }
cursor_to() { printf "$ESC[$1;${2:-1}H"; }
print_inactive() { printf "$2 $1 "; }
print_active() { printf "$2 $ESC[7m $1 $ESC[27m"; }
get_cursor_row() { IFS=';' read -sdR -p $'\E[6n' ROW COL; echo ${ROW#*[}; }
local return_value=$1
local -n options=$2
local -n defaults=$3
local selected=()
for ((i=0; i<${#options[@]}; i++)); do
if [[ ${defaults[i]} = "true" ]]; then
selected+=("true")
else
selected+=("false")
fi
printf "\n"
done
# determine current screen position for overwriting the options
local lastrow=`get_cursor_row`
local startrow=$(($lastrow - ${#options[@]}))
# ensure cursor and input echoing back on upon a ctrl+c during read -s
trap "cursor_blink_on; stty echo; printf '\n'; exit" 2
cursor_blink_off
key_input() {
local key
IFS= read -rsn1 key 2>/dev/null >&2
if [[ $key = "" ]]; then echo enter; fi;
if [[ $key = $'\x20' ]]; then echo space; fi;
if [[ $key = "k" ]]; then echo up; fi;
if [[ $key = "j" ]]; then echo down; fi;
if [[ $key = $'\x1b' ]]; then
read -rsn2 key
if [[ $key = [A || $key = k ]]; then echo up; fi;
if [[ $key = [B || $key = j ]]; then echo down; fi;
fi
}
toggle_option() {
local option=$1
if [[ ${selected[option]} == true ]]; then
selected[option]=false
else
selected[option]=true
fi
}
print_options() {
# print options by overwriting the last lines
local idx=0
for option in "${options[@]}"; do
#local prefix="[ ]"
local prefix="${gry}${deselected_opt}${dfl}"
if [[ ${selected[idx]} == true ]]; then
#prefix="[\e[38;5;46m✔\e[0m]"
prefix="${selected_opt}"
fi
cursor_to $(($startrow + $idx))
if [ $idx -eq $1 ]; then
print_active "$option" "$prefix"
else
print_inactive "$option" "$prefix"
fi
((idx++))
done
}
local active=0
while true; do
print_options $active
# user key control
case `key_input` in
space) toggle_option $active;;
enter) print_options -1; break;;
up) ((active--));
if [ $active -lt 0 ]; then active=$((${#options[@]} - 1)); fi;;
down) ((active++));
if [ $active -ge ${#options[@]} ]; then active=0; fi;;
esac
done
# cursor position back to normal
cursor_to $lastrow
printf "\n"
cursor_blink_on
eval $return_value='("${selected[@]}")'
}
# Example for above multiselect functions
#my_options=( "Option 1" "Option 2" "Option 3" )
#preselection=( "true" "true" "false" )
#multiselect result my_options preselection
#idx=0
#for option in "${my_options[@]}"; do
# echo -e "$option\t=> ${result[idx]}"
# ((idx++))
#done
### IP validation function
# returns 0 (true) for valid, 1 (false) for invalid
valid-ip()
{
local ip=$1
local stat=1
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
OIFS=$IFS
IFS='.'
ip=($ip)
IFS=$OIFS
[[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
&& ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
stat=$?
fi
return $stat
}
# Version comparison function
# usage:
# vercomp [first version] [second version]
# output:
# 0 =
# 1 >
# 2 <
vercomp() {
if [[ $1 == $2 ]]
then
return 0
fi
local IFS=.
local i ver1=($1) ver2=($2)
# fill empty fields in ver1 with zeros
for ((i=${#ver1[@]}; i<${#ver2[@]}; i++))
do
ver1[i]=0
done
for ((i=0; i<${#ver1[@]}; i++))
do
if [[ -z ${ver2[i]} ]]
then
# fill empty fields in ver2 with zeros
ver2[i]=0
fi
if ((10#${ver1[i]} > 10#${ver2[i]}))
then
# version 1 is greater than version 2
return 1
fi
if ((10#${ver1[i]} < 10#${ver2[i]}))
then
# version 1 is less than version 2
return 2
fi
done
return 0
}
compare-versions() {
vercomp $1 $2
case $? in
0) op='=';;
1) op='>';;
2) op='<';;
esac
if [[ $op != $3 ]]
then
echo "Fail: Expected '$3', Actual '$op', Arg1 '$1', Arg2 '$2'"
else
echo "Pass: '$1 $op $2'"
fi
}
set-boxtype