319 lines
8.2 KiB
Bash
Executable File
319 lines
8.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Ctlos Linux https://ctlos.github.io
|
|
#
|
|
# Dot file management script using zenity, git & rsync
|
|
# Written by Nathaniel Maia, November 2017 - March 2018
|
|
|
|
# Config ~/.dotsrc
|
|
# Ignore github ~/.gitignore
|
|
# git.io/ctldotsrc
|
|
# wget git.io/ctldots
|
|
# sudo cp -fv ~/.bin/dots /usr/bin/dots
|
|
|
|
# immutable values
|
|
readonly NAME=$(basename "$0")
|
|
readonly CFG="$HOME/.${NAME}rc"
|
|
|
|
readonly B='\E[1;34m'
|
|
readonly R='\E[1;31m'
|
|
readonly G='\E[1;32m'
|
|
readonly N='\E[0m'
|
|
readonly BLD='\E[1m'
|
|
|
|
readonly WRN="${R}[WARN]${N}:"
|
|
readonly INF="${B}[INFO]${N}:"
|
|
readonly YN="${B}[${R}y${B}/${R}${BLD}N${N}${B}]${N}: "
|
|
|
|
readonly MENUTXT="\n Enter number or letter and press [Enter]. 0/q to cancel\n\n> "
|
|
|
|
readonly MENU="
|
|
\n\t ${R}1 ${N}| ${R}${BLD}b${N}ackup\t\t-> Backup Files
|
|
\t${B}------------------------------------------${N}
|
|
\n\t ${R}2 ${N}| ${R}${BLD}r${N}estore\t\t-> Restore Files
|
|
\t${B}------------------------------------------${N}"
|
|
|
|
|
|
main_menu() {
|
|
local choice
|
|
|
|
clear
|
|
echo -e "$MENU"
|
|
draw_box 12
|
|
printf "$MENUTXT"
|
|
read -e -r choice
|
|
clear
|
|
|
|
[[ $choice =~ (v|V) ]] && V="v"
|
|
|
|
case "$choice" in
|
|
0|[Qq]|quit) exit 0 ;;
|
|
1*|[Bb]*|backup*) [[ $BASE_DIR ]] && { backup_files; exit 0; } || edit_config ;;
|
|
2*|[Rr]*|restore*) restore_files && exit 0
|
|
esac
|
|
|
|
src_cfg
|
|
main_menu
|
|
}
|
|
|
|
draw_box() {
|
|
local h=${1:-12} # $1 Box Height
|
|
local w=${2:-57} # $2 Box Width
|
|
local row=${3:-1} # $3 Starting Row
|
|
local col=${4:-2} # $4 Starting Column
|
|
local co=${5:-1} # $5 Box Color, 1-7
|
|
((h--)) # account for corner
|
|
((w--)) # account for corner
|
|
local endrow=$((row + h)) # end row
|
|
local endcol=$((col + w)) # end column
|
|
echo -ne "\E[3${co}m" # foreground colour
|
|
|
|
local hz="-" vt="|" cn="+" # horizontal, vertical, and corner chars
|
|
|
|
plot_char() {
|
|
echo -e "\E[${1};${2}H""$3"
|
|
}
|
|
|
|
local i=0
|
|
for ((r=row; i<h; r++)); do
|
|
plot_char "$r" "$col" "$vt" # left side
|
|
((i++))
|
|
done
|
|
i=0
|
|
for ((r=row; i<h; r++)); do
|
|
plot_char "$r" "$endcol" "$vt" # right side
|
|
((i++))
|
|
done
|
|
i=0
|
|
for ((c=col; i<w; c++)); do
|
|
plot_char "$row" "$c" "$hz" # top side
|
|
((i++))
|
|
done
|
|
i=0
|
|
for ((c=col; i<w; c++)); do
|
|
plot_char "$endrow" "$c" "$hz" # bottom side
|
|
((i++))
|
|
done
|
|
|
|
plot_char "$row" "$col" "$cn" # left-top corner
|
|
plot_char "$row" "$endcol" "$cn" # right-top corner
|
|
plot_char "$endrow" "$col" "$cn" # left-bottom corner
|
|
plot_char "$endrow" "$endcol" "$cn" # right-bottom corner
|
|
tput sgr0 # Reset colours
|
|
}
|
|
|
|
check_reqs() {
|
|
if ! hash git rsync &>/dev/null && hash pacman &>/dev/null; then
|
|
echo -e "\n\n\n\tInstalling:\n\t\tgit\n\t\trsync\n\n\tPlease Wait.."
|
|
draw_box 8
|
|
sleep 1
|
|
clear
|
|
sudo pacman -S git rsync --noconfirm --needed
|
|
fi
|
|
}
|
|
|
|
prep_directory() {
|
|
if [[ $REQ != "True" ]]; then
|
|
check_reqs
|
|
if [[ $BASE_DIR ]]; then # is set in config but might not exist yet
|
|
if [[ ! -d $BASE_DIR ]]; then # is not an existing dir
|
|
|
|
if [[ $REPO ]] && grep -wq '^https://.*/.*/.*$' <<< "$REPO"; then # is REPO actually and address
|
|
git clone "$REPO" "$BASE_DIR"
|
|
else
|
|
mkdir -p$V "$BASE_DIR" # local backup
|
|
fi
|
|
fi
|
|
|
|
[[ -d $BASE_DIR ]] && REQ="True"
|
|
else
|
|
echo -e "$WRN BASE_DIR must be set in config to continue. $EXITING"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
clean_backup_dirs() {
|
|
if [[ ${PREV_BACKUPS[*]} ]] && sub_choice "Perform clean backup (wipe BASE_DIR)" "${R}$BASE_DIR${N}"; then
|
|
for dir in "${PREV_BACKUPS[@]}"; do
|
|
if [[ -d $dir ]] && sub_choice "Wipe all files in /$(basename "$dir")" "${R}$dir${N}"; then
|
|
cd "$dir"
|
|
git rm -r -f .
|
|
# rm -rf$V "$dir"
|
|
[[ $V ]] && sleep 0.5
|
|
fi
|
|
done
|
|
fi
|
|
}
|
|
|
|
commit_changes() {
|
|
if grep -q 'https://' <<< "$REPO"; then
|
|
if sub_choice "Commit and push changes to REPO" "${R}$REPO${N}"; then
|
|
|
|
printf "\nEnter commit message below, an example has been provided\n\n> "
|
|
read -e -i "$(date +%Y.%m.%d) Update " -r comment
|
|
|
|
if grep -wq '[a-zA-Z0-9]*' <<< "$comment"; then
|
|
cd "$BASE_DIR/" || return 1
|
|
git add .
|
|
git commit -m "$comment"
|
|
git push origin "${BRANCH:-HEAD}"
|
|
else
|
|
echo "Bad commit message"
|
|
sleep 1
|
|
commit_changes
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
backup_files() {
|
|
prep_directory
|
|
clean_backup_dirs
|
|
|
|
if [[ ${USER_PATHS[*]} || ${ROOT_PATHS[*]} ]]; then
|
|
|
|
echo -e "$INF ${B}Copying files, please wait..$N\n"
|
|
for f in "${USER_PATHS[@]}"; do
|
|
[[ -e $f ]] && rsync -aR$V $f "$BASE_DIR/$USER_DIR/"
|
|
[[ $V ]] && sleep 0.5
|
|
done
|
|
|
|
for f in "${ROOT_PATHS[@]}"; do
|
|
[[ -e $f ]] && rsync -aR$V $f "$BASE_DIR/$ROOT_DIR/"
|
|
[[ $V ]] && sleep 0.5
|
|
done
|
|
|
|
echo -e "$INF ${G}Backup complete$N"
|
|
sleep 1
|
|
else
|
|
echo -e "$WRN No valid file paths were found.."
|
|
sleep 2
|
|
fi
|
|
commit_changes
|
|
}
|
|
|
|
restore_files() {
|
|
prep_directory
|
|
if [[ ${PREV_BACKUPS[*]} ]]; then
|
|
local msg=""
|
|
|
|
for x in "${PREV_BACKUPS[@]}"; do
|
|
if [[ -e $x ]] && sub_choice "Restore everything from /$(basename "$x")" "${R}$x${N}"; then
|
|
if grep -q "$BASE_DIR/$USER_DIR" <<< "$x"; then
|
|
rsync -avPC --filter="exclude $ROOT_DIR" "$BASE_DIR/$USER_DIR/" "$HOME/"
|
|
elif grep -q "$BASE_DIR/$ROOT_DIR" <<< "$x"; then
|
|
sudo rsync -avn "$BASE_DIR/$ROOT_DIR/" /
|
|
else
|
|
echo -e "\n\n\n\t$WRN Unable to restore\n\n\t$x"
|
|
draw_box 9
|
|
sleep 1
|
|
fi
|
|
|
|
fi
|
|
|
|
[[ $V ]] && sleep 0.5
|
|
done
|
|
|
|
else
|
|
echo -e "\n\n\n\t$INF ${R}No Existing backups to restore${N}"
|
|
draw_box 9
|
|
sleep 1
|
|
fi
|
|
}
|
|
|
|
mk_cfg() {
|
|
[[ $1 == "new" ]] && edit_config
|
|
|
|
for f in "${USER_PATHS[@]}"; do
|
|
if [[ -e $f ]] && ! grep "$f" <<< "$u_paths"; then
|
|
u_paths="$u_paths\"$f\"\n"
|
|
fi
|
|
done
|
|
|
|
for f in "${ROOT_PATHS[@]}"; do
|
|
if [[ -e $f ]] && ! grep "$f" <<< "$r_paths"; then
|
|
r_paths="$r_paths\"$f\"\n"
|
|
fi
|
|
done
|
|
|
|
local cfg="# config file for dfm (dotfile manager)
|
|
\n# repo https address for cloning & pushing (empty for local backup)
|
|
REPO=\"$REPO\"
|
|
\n# branch, defaults to current branch (HEAD)
|
|
BRANCH=\"$BRANCH\"
|
|
\n# location where backup folder or repo will be created or cloned
|
|
BASE_DIR=\"$BASE_DIR\"
|
|
\n# names for storage directories within BASE_DIR.
|
|
# created only if needed, stores files from below arrays
|
|
USER_DIR=\"${USER_DIR}\"
|
|
ROOT_DIR=\"${ROOT_DIR:-root}\"
|
|
\n# file paths which will be backed up into the directories above
|
|
USER_PATHS=(\n$u_paths)
|
|
\nROOT_PATHS=(\n$r_paths)"
|
|
|
|
echo -e "$cfg" > "$CFG" && clear
|
|
src_cfg
|
|
}
|
|
|
|
edit_config() {
|
|
$EDITOR "$CFG"
|
|
src_cfg
|
|
}
|
|
|
|
src_cfg() {
|
|
PREV_BACKUPS=()
|
|
! . "$CFG" 2>/dev/null && mk_cfg "new"
|
|
[[ -d $BASE_DIR/$USER_DIR ]] && PREV_BACKUPS+=("$BASE_DIR/$USER_DIR")
|
|
[[ -d $BASE_DIR/$ROOT_DIR ]] && PREV_BACKUPS+=("$BASE_DIR/$ROOT_DIR")
|
|
}
|
|
|
|
sub_choice() {
|
|
local choice
|
|
|
|
clear
|
|
printf "\n\n\n\t$1? $YN\n\n\t$2"
|
|
draw_box 9
|
|
tput cup 3 $((${#1} + 17))
|
|
read -r choice
|
|
clear
|
|
|
|
grep -q '[yY]' <<< "$choice" && return 0 || return 1
|
|
}
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
|
|
USAGE:
|
|
$NAME [OPTIONS..]
|
|
|
|
OPTIONS:
|
|
|
|
-h,--help Print this usage message
|
|
-v,--verbose More information output
|
|
|
|
Without any options the main dialog menu opens.
|
|
|
|
INFO:
|
|
Configuration is done in: '$CFG'
|
|
|
|
A default config will be created if one is not found,
|
|
followed by opening it in $EDITOR for you to configure.
|
|
|
|
Alternatively open your favorite editor and edit it manually.
|
|
|
|
EOF
|
|
exit 0
|
|
}
|
|
|
|
for arg in "$@"; do
|
|
case $arg in
|
|
-v|--verbose) V="v" ;;
|
|
-h|--help) usage ;;
|
|
esac
|
|
done
|
|
|
|
src_cfg
|
|
main_menu
|
|
|
|
exit 0
|