diff --git a/functions b/functions new file mode 100644 index 0000000..b1d12ab --- /dev/null +++ b/functions @@ -0,0 +1,395 @@ +#!/bin/bash +# pyr0ball script functions library + +# Initial Vars +scriptname="${0##*/}" +rundir="${0%/*}" +runuser="$(whoami)" +pretty_date="`date +%Y-%m-%d_%H-%M-%S`" + +# 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") + +# 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="single" +borderchar="#" + +# Colorization options +if [[ "$TERM" != "linux" ]] ; then + red=$(echo -e "${ESC}[38;5;1m") # red + grn=$(echo -e "${ESC}[38;5;2m") # green + ylw=$(echo -e "${ESC}[38;5;3m") # yellow + blu=$(echo -e "${ESC}[38;5;27m") # blue + lbl=$(echo -e "${ESC}[38;5;69m") # light blue + mag=$(echo -e "${ESC}[38;5;5m") # magenta + cyn=$(echo -e "${ESC}[38;5;6m") # cyan + pur=$(echo -e "${ESC}[38;5;135m") # purple + ong=$(echo -e "${ESC}[38;5;166m") # orange + lyl=$(echo -e "${ESC}[38;5;228m") # light yellow + lrd=$(echo -e "${ESC}[38;5;196m") # light red + norm=$(echo -e "${ESC}[38;5;9m") # default/normal + # + bld=$(echo -e "${ESC}[1m") # bold + unl=$(echo -e "${ESC}[4m") # underline + blk=$(echo -e "${ESC}[7m") # blinking not supported in our xterm, so reverse instead + nln=$(echo -e "${ESC}[24") # not-underline + dfl=$(echo -e "${ESC}[0m") # restore default +fi + +# Extra Unicode Character Manipulation +return_arrow=$(echo -e '\u21B6') +enter_arrow$(echo -e '\u21B5') + +# For drawing pretty boxes +terminal_width=$(tput cols) +if [ $terminal_width -le 81 ] ; then + BOXWIDTH=$((terminal_width - 1)) +else + BOXWIDTH=80 +fi + +# Box border type single-line +box-single(){ + # ---------------------------------------# + top_border=$(echo -e '\u2501') + left_border=$(echo -e '\u2503') + right_border=$(echo -e '\u2503') + 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 border type double-line +box-double(){ + # ---------------------------------------# + top_border=$(echo -e '\u2501') + left_border=$(echo -e '\u2503') + right_border=$(echo -e '\u2503') + 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-singlechar(){ + # ---------------------------------------# + top_border=$borderchar + left_border=$borderchar + right_border=$borderchar + left_top_border=$borderchar + right_top_border=$borderchar + left_bottom_border=$borderchar + right_bottom_border=$borderchar + # ---------------------------------------# +} + +case $boxtype in + single) box-single ;; + double) box-double ;; + char) box-singlechar ;; +esac + +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 "$top_border" $((BOXWIDTH-1)) + echo -n "$right_bottom_border" + echo +} + +boxline() { + echo -e "$left_border $1\r${ESC}[${BOXWIDTH}C$right_border" +} + +boxseparator(){ + echo -n "$left_border" + repchar "-" $((BOXWIDTH-1)) + echo -n "$right_border" + echo +} + +boxborder(){ + boxtop + for line in "$@" ; do + boxline "$line" + done + boxbottom +} + +success(){ + echo -e "\n" + boxborder " ${scriptname} ${grn}SUCCESS${dfl}$@" + exit 0 +} + +warn(){ + echo -e "\n" + ec=$? + boxtop + boxline "${lrd}WARNING${lyl}[${ong}code=${red}$ec${lyl}]: $@${dfl}" + boxbottom +} + +fail(){ + ec=$? + echo -e "\n" + boxtop + boxline "${lrd}FAILED${lyl}[${ong}code=${red}$ec${lyl}]: $@${dfl}" + boxbottom + exit $ec +} + +pushd(){ + command pushd "$@" > /dev/null +} + +popd(){ + command popd "$@" > /dev/null +} + +popdfail(){ + ec=$? + popd + $(exit $ec) #restore the exit code + fail "$@" +} + +logger(){ + #$@ 2>&1 | tee >(ts "[$scriptname][%d-%m-%y %H_%M_%S]" > $logfile) # This version of prepend requires the 'ts' utility from 'moreutils' package + $@ 2>&1 | tee >(while IFS= read -r line; do printf '[%s] %s\n' "${scriptname}][$(date '+%Y-%m-%d %H:%M:%S')" "$line"; done >> $logfile) +} + +ctrl_c(){ + echo -e "\n" + fail "User interrupted with Ctrl-C" +} + +# ensure ctrl-c to cancel script ends the entire process and not just the current function +trap ctrl_c INT + +# 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="[ ]" + if [[ ${selected[idx]} == true ]]; then + prefix="[\e[38;5;46m✔\e[0m]" + 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 \ No newline at end of file