Instantly share code, notes, and snippets.
Created
April 26, 2020 18:42
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save rasmusmerzin/8ca247848e13a07c1bf9ca25ee620229 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
INDENT=4 | |
USAGE='[q] quit [j/k] move [G] submit [h/l] select [H/L] jump [A] select all' | |
DESC_HEIGHT=2 | |
err() { echo "error: $*" >&2 && exit 1; } | |
is_installed() { command -v "$*" >/dev/null; } | |
# path | |
get_file_directory() { echo "$*" | sed 's/^\(.*\)\/[^\/]\+\/\?$/\1/'; } | |
get_file_name() { echo "$*" | sed 's/^.*\/\([^\/]\+\)\/\?$/\1/'; } | |
get_file_depth() { | |
steps=$(echo "$*" | sed 's/\/$//;s/[^\/]//g') | |
echo ${#steps} | |
} | |
missingdeps= | |
for dep in sed grep tr seq sudo | |
do is_installed "$dep" || missingdeps="$dep $missingdeps" | |
done | |
if [ -n "$missingdeps" ] | |
then err "required but not installed: $missingdeps" | |
fi | |
[ "$EUID" = 0 ] && err "this script shouldn't be run as root" | |
# arr | |
join() { tr ' ' ' '; } | |
split() { tr ' ' ' '; } | |
nth() { | |
target=$1; shift | |
[ $target -lt 0 ] && target=$(($#+target)) | |
i=0 | |
for val in "$@" | |
do | |
[ $i -eq $target ] && { echo $val; break; } | |
i=$((i+1)) | |
done | |
} | |
rename() { | |
target=$1; shift | |
new_value=$1; shift | |
[ $target -lt 0 ] && target=$(($#+target)) | |
new_arr= | |
i=0 | |
for val in "$@" | |
do | |
if [ $i -eq $target ] | |
then new_arr="$new_arr $new_value" | |
else new_arr="$new_arr $val" | |
fi | |
i=$((i+1)) | |
done | |
echo "$new_arr" | |
} | |
inside() { | |
key=$(echo "$1" | split); shift | |
is=0 | |
for val in "$@" | |
do [ "$(echo "$val" | split)" = "$key" ] && is=1 && break | |
done | |
test $is -eq 1 | |
} | |
# pkg | |
if is_installed pacman | |
then | |
pkg_update='command pacman -Syu --noconfirm' | |
pkg_add='command pacman -S --noconfirm --needed' | |
pkg_is='command pacman -Si >/dev/null 2>/dev/null' | |
export pkg_update pkg_add pkg_is | |
elif is_installed apt-get | |
then | |
pkg_update='command apt-get update -y && command apt-get upgrade -y' | |
pkg_add='command apt-get install -y' | |
pkg_is='command apt-get show >/dev/null 2>/dev/null' | |
export pkg_update pkg_add pkg_is | |
else err 'currently supported package managers: apt-get; pacman -- of which none are installed' | |
fi | |
# config | |
pull_config() { | |
domain=$1 | |
author=$2 | |
config=$3 | |
{ [ -d "$HOME/.config/$config" ] || [ -f "$HOME/.config/$config" ]; } && return | |
mkdir -p ~/.config | |
cd ~/.config || return | |
git clone "https://$domain/$author/config-$config" || return | |
mv "config-$config" "$config" | |
cd "$config" || return | |
[ -f 'init.sh' ] && ./init.sh | |
} | |
# super | |
super() { | |
while [ -z "$pass" ] || ! echo "$pass" | command sudo -S -- sh -c 'echo 2>/dev/null' | |
do | |
printf "[sudo] password for $USER: " | |
printf '\033[8m' && pass=$(sed 'q' /dev/stdin) | |
printf '\033[28m' | |
echo | |
done | |
echo "$pass" | command sudo -S -- sh -c "$*"; | |
} | |
init_module_list() { | |
cd "$(get_file_directory "$0")" || err "couldn't access module list" | |
cursor=0 | |
modules=$(find src/* | sed 's/+$//' | sort -u | join); | |
modules_name= | |
modules_depth= | |
modules_selected= | |
modules_count=0 | |
max_width=0 | |
for f in $modules | |
do modules_count=$((modules_count+1)) | |
depth=$(($(get_file_depth $f)-1)) | |
name=$(get_file_name $f | split) | |
[ ${#name} -gt $max_width ] && max_width=${#name} | |
modules_name="$modules_name $name" | |
modules_depth="$modules_depth $depth" | |
modules_selected="$modules_selected 0" | |
done | |
folds= | |
folds_count=0 | |
for level in $(echo "$modules_depth" | tr ' ' '\n' | grep -v '^0$' | sort -u) | |
do | |
prev_index=0 | |
for i in $(seq $((modules_count-1))) | |
do depth=$(nth $i $modules_depth) | |
if [ $depth -lt $level ] | |
then | |
[ $((i-prev_index)) -gt 2 ] && { | |
folds="$folds $((prev_index+1)) $((i-1))" | |
folds_count=$((folds_count+1)) | |
} | |
prev_index=$i | |
elif [ $i -eq $((modules_count-1)) ] | |
then | |
folds="$folds $((prev_index+1)) $i" | |
folds_count=$((folds_count+1)) | |
fi | |
done | |
done | |
} | |
should_render() { | |
bool=1 | |
first= | |
for f in $folds | |
do | |
if [ -z $first ] | |
then first=$f | |
else | |
last=$f | |
[ $* -ge $first ] && [ $* -le $last ] && | |
[ $(nth $((first-1)) $modules_selected) -eq 0 ] && | |
bool=0 && break | |
first= | |
fi | |
done | |
[ $bool -ne 0 ] | |
} | |
get_render_index() { | |
index=$1 | |
first= | |
for f in $folds | |
do | |
if [ -z $first ] | |
then first=$f | |
else | |
last=$f | |
decider=$((first-1)) | |
[ $1 -ge $first ] && (should_render $decider) && [ $(nth $decider $modules_selected) -eq 0 ] && { | |
if [ $1 -le $last ] | |
then index=; break | |
else index=$((index-last+first-1)) | |
fi | |
} | |
first= | |
fi | |
done | |
[ -n "$index" ] && echo $index | |
} | |
get_render_count() { | |
for i in $(seq $((modules_count-1)) -1 0) | |
do should_render $i && { get_render_index $i; break; } | |
done | |
} | |
get_body_start() { | |
echo $((2+DESC_HEIGHT)) | |
} | |
render_usage() { | |
printf '\33[;H\33[2m%s\33[0m' "$USAGE" | |
} | |
render_description() { | |
printf '\33[2;H\33[K%b' "$( | |
name=$(nth $cursor $modules_name) | |
indent=$((max_width+3)) | |
space=$(($(tput cols)-indent)) | |
selected_module=$(nth $cursor $modules) | |
desc=$([ -f "$selected_module" ] && sed -n -e 's/\s\+/ /g' -e 's/^#\s*desc\s*:\s*\(.*\)\s*$/\1/p;/^[^#]/q' "$selected_module") | |
for i in $(seq $DESC_HEIGHT) | |
do | |
if [ $i -lt $DESC_HEIGHT ] | |
then printf '\33[K\33[%dC%s\n' $indent "$(echo "$desc" | cut -b "$(((i-1)*space+1))-$((i*space))")" | |
else | |
if [ $((space*DESC_HEIGHT)) -lt ${#desc} ] | |
then printf '\33[K\33[%dC%s...\n' $indent "$(echo "$desc" | cut -b "$(((i-1)*space+1))-$((i*space-4))")" | |
else printf '\33[K\33[%dC%s\n' $indent "$(echo "$desc" | cut -b "$(((i-1)*space+1))-$((i*space))")" | |
fi | |
fi | |
done | |
printf '\33[%dA%s:' $DESC_HEIGHT "$name" | |
)" | |
} | |
render_list() { | |
for i in $(seq 0 $((modules_count-1))) | |
do | |
{ [ $# -le 0 ] || inside $i $*; } && (should_render $i) && | |
printf "\33[$(($(get_body_start)+$(get_render_index $i)));H\33[K\33[$(($(nth $i $modules_depth)*INDENT+1))C\33[D[?] %s" "$(nth $i $modules_name)" | |
done | |
} | |
render_selection() { | |
for i in $(seq 0 $((modules_count-1))) | |
do | |
{ [ $# -le 0 ] || inside $i $*; } && (should_render $i) && { | |
mark=' ' && [ $(nth $i $modules_selected) -ne 0 ] && mark='*' | |
printf "\33[$(($(get_body_start)+$(get_render_index $i)));$(($(nth $i $modules_depth)*INDENT+2))H%s" "$mark" | |
} | |
done | |
} | |
render_cursor() { | |
printf "\33[$(($(get_body_start)+$(get_render_index $cursor)));$(($(nth $cursor $modules_depth)*INDENT+2))H" | |
} | |
render_full() { | |
printf '\33[2J%b' "$( | |
render_usage | |
render_list | |
render_selection | |
render_description | |
render_cursor | |
)" | |
} | |
down() { | |
for i in $(seq $((cursor+1)) $((modules_count-1))) | |
do should_render $i && { cursor=$i; break; } | |
done | |
printf '%b' "$(render_description; render_cursor)" | |
} | |
last() { | |
for i in $(seq $((modules_count-1)) -1 0) | |
do should_render $i && { cursor=$i; break; } | |
done | |
printf '%b' "$(render_description; render_cursor)" | |
} | |
up() { | |
for i in $(seq $((cursor-1)) -1 0) | |
do should_render $i && { cursor=$i; break; } | |
done | |
printf '%b' "$(render_description; render_cursor)" | |
} | |
first() { | |
cursor=0 | |
printf '%b' "$(render_description; render_cursor)" | |
} | |
select_module() { | |
modules_selected=$(rename $cursor 1 $modules_selected) | |
render_full | |
} | |
deselect_module() { | |
modules_selected=$(rename $cursor 0 $modules_selected) | |
render_full | |
} | |
toggle_module() { | |
current_state=$(nth $cursor $modules_selected) | |
current_state=$((1-current_state)) | |
modules_selected=$(rename $cursor $current_state $modules_selected) | |
render_full | |
} | |
execute_modules() { | |
cd "$(get_file_directory "$0")" || err "couldn't access module list" | |
chosen_modules= | |
chosen_modules_name= | |
chosen_modules_count=0 | |
chosen_modules_max_width=0 | |
i=0 | |
for f in $modules | |
do | |
{ | |
[ -z "$modules_selected" ] || | |
[ $(nth $i $modules_selected) -ne 0 ] && (should_render $i) | |
} && [ -f "$f" ] && { | |
chosen_modules="$chosen_modules $f" | |
chosen_modules_count=$((chosen_modules_count+1)) | |
# name=$(nth $i $modules_name) | |
name=$(get_file_name $f | split) | |
chosen_modules_name="$chosen_modules_name $name" | |
[ ${#name} -gt $chosen_modules_max_width ] && chosen_modules_max_width=${#name} | |
} | |
i=$((i+1)) | |
done | |
if [ -n "$chosen_modules" ] | |
then | |
cleanup | |
cleanup() { printf '\n\33[28m'; exit; } | |
echo "Executing modules:$chosen_modules" | |
super | |
execution_times= | |
for m in $chosen_modules | |
do | |
begin_time=$(date +%s) | |
{ | |
if [ -n "$(cat "$m")" ] | |
then . "$m" | |
else super "$pkg_add $(get_file_name "$m")" | |
fi | |
} 2>&1 | sed "s|^|\o33[1m$m:\o33[0m|" | |
end_time=$(date +%s) | |
execution_times="$execution_times $((end_time-begin_time))" | |
done | |
echo | |
for i in $(seq 0 $((chosen_modules_count-1))) | |
do printf "%-$((chosen_modules_max_width+1))s%6ds\n" "$(nth $i $chosen_modules_name)" "$(nth $i $execution_times)" | |
done | |
true | |
else | |
cleanup | |
echo 'No modules selected' | |
false | |
fi | |
} | |
main() { | |
init_module_list | |
render_full | |
cols=$(tput cols) | |
lines=$(tput lines) | |
ansi_state=0 | |
while read -rsn1 KEY | |
do | |
if [ "$(printf '\33')" = "$KEY" ] | |
then | |
if [ $ansi_state -eq 0 ] | |
then ansi_state=1 | |
else ansi_state=0 | |
fi | |
elif [ '[' = "$KEY" ] | |
then | |
if [ $ansi_state -eq 1 ] | |
then ansi_state=2 | |
else ansi_state=0 | |
fi | |
else | |
case "$KEY" in | |
q) cleanup && exit ;; | |
k) up ;; | |
j) down ;; | |
l) select_module ;; | |
h) deselect_module ;; | |
L) last ;; | |
H) first ;; | |
A) | |
if [ $ansi_state -eq 2 ] | |
then up | |
else | |
if inside 0 $modules_selected | |
then | |
modules_selected= | |
for i in $(seq $modules_count) | |
do modules_selected="$modules_selected 1" | |
done | |
else | |
modules_selected= | |
for i in $(seq $modules_count) | |
do modules_selected="$modules_selected 0" | |
done | |
should_render $cursor || up | |
fi | |
render_full | |
fi | |
;; | |
B) [ $ansi_state -eq 2 ] && down ;; | |
C) [ $ansi_state -eq 2 ] && select_module ;; | |
D) [ $ansi_state -eq 2 ] && deselect_module ;; | |
$(printf '\n')) toggle_module ;; | |
G) | |
if execute_modules | |
then break | |
else render_cursor | |
fi | |
;; | |
*) ;; | |
esac | |
ansi_state=0 | |
fi | |
old_cols=$cols | |
old_lines=$lines | |
cols=$(tput cols) | |
lines=$(tput lines) | |
{ [ $old_cols != $cols ] || [ $old_lines != $lines ]; } && render_full | |
done | |
} | |
cleanup() { printf '\n\33[28m'; } | |
trap 'cleanup; exit' SIGINT SIGTERM | |
if [ $# -eq 0 ] | |
then | |
cleanup() { printf "\33[28m\33[$(($(get_body_start)+$(get_render_count)+2));H"; } | |
main | |
else | |
modules=$* | |
execute_modules | |
fi | |
pass= |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment