#!/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) 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 # ---------------------------------------# 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') # ---------------------------------------# 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" boxtop boxline " ${scriptname} ${grn}SUCCESS${dfl}$@" boxbottom 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