#!/usr/bin/env bash
##
# Split a string into an array of arguments
# @example str_split_args "One two three" args_list # $args_list will contain three elements, 'One','two', and 'three'
# @arg $1 the string to split
# @arg $2 variable name to store array in
#
function str_split_args(){
local -n ARGS_ARRAY="$2"
local arg_list
IFS=' ' read -r -a ARGS_ARRAY <<< "$1"
}
##
# Move the file to a dated trash dir
# @arg $1 relative path to the file, within the data directory
#
function trash_file(){
echo "$trash_dir"
local src_file="$data_dir/$1"
local target_file="$trash_dir/$1"
local target_dir="$(dirname "$target_file")"
mkdir --parents "$target_dir"
mv "$src_file" "$target_file"
}
##
# Prompt to choose items
#
# @example select_items packages_to_remove "Choose packages to dnf remove" "${packages[@]}"
# @arg $1 name of variable to store items in
# @arg $2 header for the prompt
# @arg $3 array of items
#
function select_items(){
local -n SELECTED_ITEMS="$1"
local promptstr
local header="$2"
local items=()
local index=0
for arg in "$@";do
if [[ $index -lt 2 ]];then
index=$((index + 1))
continue;
fi
items+=("$arg")
done
# echo "First:${items[0]}"
# echo "All:${items[@]}"
# exit;
promptstr=""
promptstr=()
for item in "${items[@]}";do
promptstr+=("$item")
promptstr+=("$item")
promptstr+=("")
done
prompt_choose_multi SELECTED_ITEMS "# $header" "${promptstr[@]}"
}
##
# split a file by string and filter out blank lines and lines starting with a `#` or space
#
# @example get_file_items "$file" packages
# @arg $1 the absolute path to the file to load
# @arg $2 name of variable to store the items array in
#
function get_file_items(){
local file="$1"
local -n ITEMS="$2"
content=$(cat "$file")
lines=()
str_split_line "$content" lines
ITEMS=""
ITEMS=()
for line in "${lines[@]}";do
if [[ "${line:0:1}" == "#" || "${line:0:1}" == " " || -z "$line" ]];then
continue;
fi
ITEMS+=("$line")
done
}
##
# Source each file in a directory. Do not descend into subdirectories
# @arg $1 the relative directory inside the setup dir
#
function src_each(){
local dir;
dir="$setup_dir/$1"
prompt_yes_or_no "Source files in $dir?" || return
echo "Dir:$dir"
for f in "$dir"/*;do
if [[ -f "$f" ]];then
source "$f"
fi;
done
}
##
# remove/uninstall each package listed in the given file
# @arg $1 the relative path to the file containing a list of packages, one on each line
#
function remove_each(){
local file="$setup_dir/$1"
local packages
local packages_to_remove
local package_list
get_file_items "$file" packages
select_items packages_to_remove "Choose packages to dnf remove" "${packages[@]}"
if [[ -z "$packages_to_remove" ]];then
msg_notice "No packages selected to remove"
else
package_list="${packages_to_remove[@]}"
msg_instruct "[sudo dnf remove $package_list]"
sudo dnf remove ${packages_to_remove[@]}
fi
}
##
# install each package listed in the given file
# @arg $1 the relative path to the file containing a list of packages, one on each line
#
function install_each(){
local file="$setup_dir/$1"
local packages
local packages_to_install
local package_list
get_file_items "$file" packages
select_items packages_to_install "Choose packages to dnf install" "${packages[@]}"
if [[ -z "$packages_to_install" ]];then
msg_notice "No packages selected to install"
else
package_list="${packages_to_install[@]}"
msg_instruct "[sudo dnf install $package_list]"
sudo dnf install ${packages_to_install[@]}
fi
}
##
# decompress each file in the list.
# Each file in the list must be relative to the data directory.
# Each file must be a path WITHOUT `.tar.gz`
# the "$file.tar.gz" will be decompressed to "$file"
# "$file.tar.gz" will be removed after decompression
#
# @arg $1 the relative path to a file containing a list of files, on eon each line.
#
function decompress_each(){
local file="$setup_dir/$1"
local rel_files
local files_to_decompress
local path
get_file_items "$file" rel_files
select_items files_to_decompress "Choose Files to Decompress" "${rel_files[@]}"
if [[ -z "$files_to_decompress" ]];then
msg_notice "No files to decompress"
return;
fi
for file in "${files_to_decompress[@]}";do
dir_path="$data_dir/$file"
tar_path="${dir_path}.tar.gz"
echo "TAR:$tar_path"
echo "DIR:$dir_path"
# exit
if [[ ! -f "$tar_path" ]];then
echo "why continue?"
continue;
fi
tar -xf "$tar_path" -C "$(dirname $tar_path)"
trash_file "$file.tar.gz"
done
}
##
# Source/run each file in a directory. Do not descend into subdirectories
# @arg $1 the relative directory inside the setup dir
# @alias of src_each
function run_each(){
local dir;
dir="$setup_dir/$1"
prompt_yes_or_no "Source files in $dir?" || return
echo "Dir:$dir"
for f in "$dir"/*;do
if [[ -f "$f" ]];then
source "$f"
fi;
done
}
##
# Create a symlink for each line in the file
#
# @arg $1 the file, relative to the setup dir, which should be scanned for links
#
function link_each(){
local file="$setup_dir/$1"
local symlink
get_file_items "$file" symlink_rows
# echo "File: $file"
# echo "SYMLINK ROWS: ${symlink_rows[0]}"
# exit
select_items symlinks_to_make "Choose which symlinks to make" "${symlink_rows[@]}"
if [[ -z "$symlinks_to_make" ]];then
msg_notice "No symlinks selected to run"
else
for symlink in "${symlinks_to_make[@]}";do
# echo "LINK: $symlink"
str_split_args "$symlink" symlink_args
# echo "Args: ${symlink_args[@]}"
# echo "Arg1: ${symlink_args[0]}"
execute_symlink "${symlink_args[@]}"
done
fi
}
##
# Create a symlink in the home dir pointing to a file in the data dir
#
# @example execute_symlink config/bashrc .bashrc # create $home_dir/.bashrc pointing to $data_dir/config/bashrc
# @example config/.config -r # for every file in $data_dir/config/.config, create a same-pathed symlink in $home_dir/.config pointing to that file
#
# @arg $1 the file in the data directory that should be symlinked to
# @arg $2 (optional) The location of the symlink itself, inside the home directory
#
# @option -r recurse into source directory and only symlink to files
# @option -d delete target directory if it exists and replace with a symlink
#
function execute_symlink(){
local data_file
local home_file
local recurse="false"
local delete="false"
local arg_list
arg_list=()
for arg in "${@}";do
if [[ "$arg" == "-r" ]];then
recurse="true"
elif [[ "$arg" == "-d" ]];then
delete="true"
else
arg_list+=("$arg")
fi
done
data_file="${arg_list[0]}"
home_file="${arg_list[1]}"
if [[ -z "$home_file" ]];then
home_file="$(basename "$data_file")"
fi
echo " Data:$data_file"
echo " home:$home_file"
echo " Recurse:$recurse"
echo " Delete:$delete"
data_path="$data_dir/$data_file"
echo "Data Path: $data_path"
if [[ "$recurse" != "true" ]];then
echo "NOT RECURSE"
if [[ -d "$data_path" ]];then
echo "CREATE DIR LINK"
home_path="$home_dir/$home_file"
offer_trash_home_path "$home_path" "$data_path"
ln -s "$data_path" "$home_path"
elif [[ -f "$data_path" ]];then
echo "CREATE FILE LINK"
home_path="$home_dir/$home_file"
offer_trash_home_path "$home_path" "$data_path"
ln -s "$data_path" "$home_path"
elif [[ "${data_path:(-2)}" == "/*" ]];then
if [[ "${home_file:(-1)}" == "*" ]];then
home_file="${home_file::-1}"
fi
echo "CREATE MULTIPLE LINKS IN DIR"
data_dir_path="${data_path::-2}"
for file in "$data_dir_path"/*;do
echo "Create link to: $file"
basename="$(basename $file)"
home_path="$home_dir/$home_file/$basename"
offer_trash_home_path "$home_path" "$file"
ln -s "$file" "$home_path"
done
fi
elif [[ "$recurse" == "true" ]];then
echo "RECURSE"
local rel_path="$1"
local input_dir_path="${root_dir}/$rel_path"
# echo "$root_dir"
# echo "$input_dir_path"
### find every file
local cur_dir="$(pwd)"
# cd "$home_dir"
msg_instruct "homelink_deep $rel_path"
local file_list=()
while IFS= read -r -d $'\0' filePath; do
if [[ -d "$filePath" ]];then continue; fi
file_list+=("$filePath")
done < <(find "$data_path"/ -type f -print0)
for filePath in "${file_list[@]}";do
# echo "$filePath"
rel_file_path="${filePath##${data_path}/}"
target_home_path="${home_dir}/${rel_file_path}"
offer_trash_home_path "$target_home_path" "$filePath"
msg_status "mkdir \"$(dirname ${target_home_path})\""
mkdir --parents "$(dirname ${target_home_path})"
msg_status " then: ln -s ${filePath} ${target_home_path}"
ln -s "${filePath}" "${target_home_path}"
done
# cd "$cur_dir"
fi
exit
msg_warn "This method is not implemented yet"
}
##
# Offer to move a target file to trash in order to create a symlink
# @arg trash_path the absolute path to the file that will be replaced by a symlink
# @arg symlink_target the absolute path the symlink will point to
#
# @usage offer_trash_home_path "$path_to_trashable_file" "$path_to_symlink_target"
#
function offer_trash_home_path(){
trash_path="$1"
symlink_target="$2"
if [[ ! -e "$trash_path" ]];then
return
fi
prompt_yes_or_no "Trash '$trash_path' and replace with symlink to '$symlink_target'" allow_trash
if [[ $allow_trash ]];then
local src_file="$trash_path"
local trash_file_target="$trash_dir/${src_file##$home_dir}"
local target_dir="$(dirname "$trash_file_target")"
echo "Move to trash: $trash_file_target"
mkdir --parents "$target_dir"
mv "$src_file" "$trash_file_target"
fi
}