Firefox-UI-Fix/install.sh

674 lines
18 KiB
Bash
Executable file

#!/usr/bin/env bash
#** Helper Utils ***************************************************************
#== Message ====================================================================
lepton_error_message() {
>&2 echo "FAILED: ${@}"
exit 1
}
lepton_ok_message() {
local SIZE=50
local FILLED=""
for ((i=0; i<=$((SIZE - 2)); i++)); do
FILLED+="."
done
FILLED+="OK"
local message="${@}"
echo "${message}${FILLED:${#message}}"
}
lepton_spinner() {
local chars="/-\|"
for (( i=0; i<${#chars}; i++ )); do
sleep 0.5
echo -en "${chars:$i:1}" "\r"
done
}
#== Required Tools =============================================================
PACAPT_PATH="/usr/local/bin/pacapt"
PACAPT_INSTALLED=true
pacapt_install() {
if ! [ -x "$(command -v pacapt)" ]; then
echo "Universal Package Manager(icy/pacapt) Download && Install(need sudo permission)"
echo "It is installed temporarily and will be removed when installation is complete."
sudo curl https://github.com/icy/pacapt/raw/ng/pacapt -Lo "${PACAPT_PATH}"
sudo chmod 755 "${PACAPT_PATH}"
sudo ln -sv "${PACAPT_PATH}" /usr/local/bin/pacman || true
PACAPT_INSTALLED=false
fi
sudo pacapt -Sy
}
pacapt_uninstall() {
if [[ "${PACAPT_INSTALLED}" == false ]]; then
sudo rm -rf "${PACAPT}"
fi
}
mac_command_line_developer_tools() {
# https://unix.stackexchange.com/questions/408280/until-statement-waiting-for-process-to-finish-being-ignored
XCODE_MESSAGE="$(osascript -e 'tell app "System Events" to display dialog "Please click install when Command Line Developer Tools appears"')"
if [ "$XCODE_MESSAGE" = "button returned:OK" ]; then
xcode-select --install
else
lepton_error_message "You have cancelled the installation, please rerun the installer."
fi
until [ "$(xcode-select -p 1>/dev/null 2>&1; echo $?)" -eq 0 ]; do
lepton_spinner
done
echo ""
lepton_ok_message "Installed Command Line Developer Tools"
}
git_check() {
if ! [ -x "$(command -v git)" ]; then
if [ "${OSTYPE}" == "linux-gnu" ] || [ "${OSTYPE}" == "FreeBSD" ]; then
pacapt_install
sudo pacapt -S git
pacapt_uninstall
elif [[ "${OSTYPE}" == "darwin"* ]]; then
mac_command_line_developer_tools
else
lepton_error_message "OS NOT DETECTED, couldn't install required packages"
fi
fi
if [[ "${OSTYPE}" == "darwin"* ]]; then
if ! [ "$(git --help 1>/dev/null 2>&1; echo $?)" -eq 0 ]; then
mac_command_line_developer_tools
fi
fi
lepton_ok_message "Required - git"
}
#== PATH / File ================================================================
currentDir=$( cd "$(dirname $0)" ; pwd )
paths_filter() {
local pathListName="$1" # array name
local option="$2"
# Set array
eval "local pathList=(\"\${${pathListName}[@]}\")"
# Set default option
if [ -z "$option" ]; then
option="-e"
fi
# Check path
local foundedTargets=()
for checkTarget in "${pathList[@]}"; do
if [ "$option" "$checkTarget" ]; then
foundedTargets+=("$checkTarget")
fi
done
# Replace
eval "${pathListName}=(\"\${foundedTargets[@]}\")"
}
autocopy() {
local file="${1}"
local target="${2}"
if [ "${file}" == "${target}" ]; then
echo "'${file}' and ${target} are same file"
return 0
fi
if [ -e "${target}" ]; then
echo "${target} alreay exist."
echo "Now Backup.."
autocopy "${target}" "${target}.bak"
echo ""
fi
cp -rv "${file}" "${target}"
}
automv() {
local file="${1}"
local target="${2}"
if [ "${file}" == "${target}" ]; then
echo "'${file}' and ${target} are same file"
return 0
fi
if [ -e "${target}" ]; then
echo "${target} alreay exist."
echo "Now Backup.."
automv "${target}" "${target}.bak"
echo ""
fi
mv -v "${file}" "${target}"
}
autorestore() {
local file="${1}"
local target="${file}.bak"
if [ -e "${file}" ]; then
rm -rv "${file}"
fi
mv -v "${target}" "${file}"
local lookupTarget="${target}.bak"
if [ -e "${lookupTarget}" ]; then
autorestore "${target}"
fi
}
write_file() {
local filePath="$1"
local fileContent="$2"
echo -e "${fileContent}" | tee "${filePath}" > /dev/null
}
#== INI File ================================================================
get_ini_section() {
local filePath="$1"
local ouput=$(grep -E "^\[" "${filePath}" |sed -e "s/^\[//g" -e "s/\]$//g")
echo "${ouput}"
}
get_ini_value() {
local filePath="$1"
local key="$2"
local section="$3"
local output=""
if [ "${section}" == "" ]; then
output=$(grep -E "^${key}" "${filePath}" | cut -f 2 -d"=")
echo "${output}"
else
local sectionStart=""
for line in $(cat "${filePath}"); do
if [[ "${sectionStart}" == "true" && "${line}" == "["* ]]; then
return 0
fi
if [ "${line}" == "[${section}]" ]; then
sectionStart="true"
fi
if [ "${sectionStart}" == "true" ]; then
output=$(echo "${line}" | grep -E "^${key}" | cut -f 2 -d"=" )
if [ "${output}" != "" ]; then
echo "${output}"
fi
fi
done
fi
}
set_ini_section() {
local section="$1"
echo "[${section}]\n"
}
set_ini_value() {
local key="$1"
local value="$2"
if [ "${value}" == "" ]; then
echo ""
else
echo "${key}=${value}\n"
fi
}
#== Multiselect ================================================================
# https://stackoverflow.com/questions/45382472/bash-select-multiple-answers-at-once/54261882
multiselect() {
echo 'Select with <space>, Done with <enter>!!!'
# little helpers for terminal print control and key input
ESC=$( printf "\033")
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#*[}; }
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 = $'\x1b' ]]; then
read -rsn2 key
if [[ $key = [A ]]; then echo up; fi;
if [[ $key = [B ]]; then echo down; fi;
fi
}
toggle_option() {
local arr_name=$1
eval "local arr=(\"\${${arr_name}[@]}\")"
local option=$2
if [[ ${arr[option]} == true ]]; then
arr[option]=
else
arr[option]=true
fi
eval $arr_name='("${arr[@]}")'
}
local retval=$1
local options
local defaults
IFS=';' read -r -a options <<< "$2"
if [[ -z $3 ]]; then
defaults=()
else
IFS=';' read -r -a defaults <<< "$3"
fi
local selected=()
for ((i=0; i<${#options[@]}; i++)); do
selected+=("${defaults[i]}")
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
local active=0
while true; do
# print options by overwriting the last lines
local idx=0
for option in "${options[@]}"; do
local prefix="[ ]"
if [[ ${selected[idx]} == true ]]; then
prefix="[x]"
fi
cursor_to $(($startrow + $idx))
if [ $idx -eq $active ]; then
print_active "$option" "$prefix"
else
print_inactive "$option" "$prefix"
fi
((idx++))
done
# user key control
case `key_input` in
space) toggle_option selected $active;;
enter) 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 $retval='("${selected[@]}")'
}
#** Profile ********************************************************************
#== Profile Dir ================================================================
firefoxProfileDirPaths=(
"${HOME}/.mozilla/firefox"
"${HOME}/.var/app/org.mozilla.firefox/.mozilla/firefox"
"${HOME}/Library/Application Support/Firefox"
)
check_profile_dir() {
local profileDir="$1"
if [ "${profileDir}" != "" ]; then
firefoxProfileDirPaths=("${profileDir}")
fi
paths_filter firefoxProfileDirPaths -d
local foundCount="${#firefoxProfileDirPaths[@]}"
if [ "${foundCount}" -eq 0 ]; then
lepton_error_message "Unable to find firefox profile dir."
fi
lepton_ok_message "Profiles dir found"
}
#== Profile Info ===============================================================
PROFILEINFOFILE="profiles.ini"
check_profile_ini() {
for profileDir in "${firefoxProfileDirPaths[@]}"; do
if [ ! -f "${profileDir}/${PROFILEINFOFILE}" ]; then
lepton_error_message "Unable to find ${PROFILEINFOFILE} at ${profileDir}"
fi
done
lepton_ok_message "Profiles info file found"
}
#== Profile PATH ===============================================================
firefoxProfilePaths=()
update_profile_paths() {
local IFS=$'\n'
for profileDir in "${firefoxProfileDirPaths[@]}"; do
local escapeDir=$(echo "${profileDir}" | sed "s|\/|\\\/|g")
firefoxProfilePaths+=($(
get_ini_value "${profileDir}/${PROFILEINFOFILE}" "Path" |
sed "s/^/${escapeDir}\//"
))
done
local foundCount="${#firefoxProfilePaths[@]}"
if ! [ "${foundCount}" -eq 0 ]; then
lepton_ok_message "Profile paths updated"
else
lepton_error_message "Doesn't exist profiles"
fi
}
select_profile() {
local profileName="$1"
if [ "${profileName}" != "" ]; then
local targetPath=""
for profilePath in "${firefoxProfilePaths[@]}"; do
if [[ "${profilePath}" == *"${profileName}" ]]; then
targetPath="${profilePath}"
break
fi
done
if [ "${targetPath}" != "" ]; then
lepton_ok_message "Profile, \"${profileName}\" found"
firefoxProfilePaths=("${targetPath}")
else
lepton_error_message "Unable to find ${profileName}"
fi
else
local foundCount="${#firefoxProfilePaths[@]}"
if [ "${foundCount}" -eq 1 ]; then
lepton_ok_message "Auto detected profile"
else
local multiPaths=""
for profilePath in "${firefoxProfilePaths[@]}"; do
multiPaths+="${profilePath};"
done
multiselect profileSelected "${multiPaths}"
local targetPaths=()
for ((i=0; i<"${#profileSelected[@]}"; i++)); do
local result="${profileSelected[${i}]}"
if [ "$result" == "true" ]; then
targetPaths+=("${firefoxProfilePaths[${i}]}")
fi
done
firefoxProfilePaths=("${targetPaths[@]}")
foundCount="${#firefoxProfilePaths[@]}"
if [ "${foundCount}" -eq 0 ]; then
lepton_error_message "Please select profiles"
fi
lepton_ok_message "Multi selected profiles"
fi
fi
}
#** Install ********************************************************************
#== Install Types ==============================================================
leptonBranch="master"
select_distribution() {
select distribution in "Original(default)" "Photon-Style"; do
case "${distribution}" in
"Original") leptonBranch="master" ;;
"Photon-Style") leptonBranch="photon-style" ;;
esac
lepton_ok_message "Selected ${distribution}"
break
done
}
leptonInstallType="Network" # Other types: Local, Release
check_install_type() {
local targetListName="${1}"
local installType="${2}"
eval "local targetCount=\${#${targetListName}[@]}"
paths_filter "${targetListName}"
eval "local foundCount=\${#${targetListName}[@]}"
if [ "${targetCount}" -eq "${foundCount}" ]; then
leptonInstallType="${installType}"
fi
}
checkLocalFiles=(
userChrome.css
userContent.css
icons
)
checkReleaseFiles=(
user.js
chrome/userChrome.css
chrome/userContent.css
chrome/icons
)
check_install_types() {
check_install_type checkLocalFiles "Local"
check_install_type checkReleaseFiles "Release"
lepton_ok_message "Checked install type: ${leptonInstallType}"
if [ "${leptonInstallType}" == "Network" ]; then
select_distribution
fi
if [ "${leptonInstallType}" == "Local" ]; then
if [ -d ".git" ]; then
select_distribution
git checkout "${leptonBranch}"
fi
fi
}
#== Each Install ===============================================================
install_local() {
for profilePath in "${firefoxProfilePaths}"; do
autocopy user.js "${profilePath}/user.js"
autocopy "${currentDir}" "${profilePath}/chrome"
done
lepton_ok_message "End profile copy"
}
install_release() {
for profilePath in "${firefoxProfilePaths}"; do
autocopy user.js "${profilePath}/user.js"
autocopy chrome "${profilePath}/chrome"
done
lepton_ok_message "End profile copy"
}
install_network() {
local duplicate=""
if [ -e "chrome" ]; then
duplicate="true"
automv chrome chrome.bak
fi
git_check
git clone -b "${leptonBranch}" https://github.com/black7375/Firefox-UI-Fix.git chrome
if ! [ -d "chrome" ]; then
lepton_error_message "Unable to find downloaded files"
fi
local isProfile=""
for profilePath in "${firefoxProfilePaths}"; do
autocopy chrome/user.js "${profilePath}/user.js"
autocopy chrome "${profilePath}/chrome"
if [ "${currentDir}" == "${profilePath}" ]; then
isProfile="true"
fi
done
lepton_ok_message "End profile copy"
cd "${currentDir}"
if [ ! "${duplicate}" == "true" ]; then
rm -rv chrome
fi
if [ "${duplicate}" == "true" ]; then
autorestore chrome
fi
lepton_ok_message "End clean files"
}
install_profile() {
lepton_ok_message "Started install"
case "${leptonInstallType}" in
"Local") install_local ;;
"Release") install_release ;;
"Network") install_network ;;
esac
lepton_ok_message "End install"
}
#** Lepton Info File ***********************************************************
#== Info File format & update policy ===========================================
## `LEPTON` file format
# If this file exist in same directory as the `userChrome.css` file,
# it is recognized as the "Lepton" installation directory.
# Branch=master | photon-style
# Ver=<git tag> | <git hash> | [NULL]
## `lepton.ini` file Format
# [Profile PATH]
# Type=Local | Release | Git
# Branch=master | photon-style
# Ver=<git tag> | <git hash> | [NULL]
## Update Policy
# Type
# - Local(unknown): force latest commit update
# - Release(<git tag>): force latest tag update
# - Git<git hash>: latest commit update
#== Lepton Info ================================================================
LEPTONINFOFILE="lepton.ini"
check_lepton_ini() {
for profileDir in "${firefoxProfileDirPaths[@]}"; do
if [ ! -f "${profileDir}/${LEPTONINFOFILE}" ]; then
lepton_error_message "Unable to find ${LEPTONINFOFILE} at ${profileDir}"
fi
done
lepton_ok_message "Lepton info file found"
}
#== Create info file ===========================================================
# We should always create a new one, as it also takes into account the possibility of setting it manually.
# Updates happen infrequently, so the creation overhead is less significant.
CHROMEINFOFILE="LEPTON"
write_lepton_info() {
# Init info
local output=""
local prevDir=$(dirname "${firefoxProfilePaths[0]}")
for profilePath in "${firefoxProfilePaths[@]}"; do
local LEPTONINFOPATH="${profilePath}/chrome/${CHROMEINFOFILE}"
local LEPTONGITPATH="${profilePath}/chrome/.git"
# Profile info
local Type=""
local Ver=""
local Branch=""
if [ -f "${LEPTONINFOPATH}" ]; then
if [ -d "${LEPTONGITPATH}" ]; then
Type="Git"
Ver=$( git --git-dir "${LEPTONGITPATH}" rev-parse HEAD)
Branch=$(git --git-dir "${LEPTONGITPATH}" rev-parse --abbrev-ref HEAD)
else
Type=$( get_ini_value "${LEPTONINFOPATH}" "TYPE" )
Ver=$( get_ini_value "${LEPTONINFOPATH}" "Ver" )
Branch=$(get_ini_value "${LEPTONINFOPATH}" "Branch")
if [ "${Type}" == "" ]; then
Type="Local"
fi
fi
fi
# Flushing
local profileDir=$(dirname "${profilePath}")
local profileName=$(basename "${profilePath}")
if [ "${prevDir}" != "${profileDir}" ]; then
write_file "${prevDir}/${LEPTONINFOFILE}" "${output}"
output=""
fi
# Make output contents
if [ -f "${LEPTONINFOPATH}" ]; then
output="${output}$(set_ini_section ${profileName})"
local Path="${profilePath}"
for key in "Type" "Branch" "Ver" "Path"; do
eval "local value=\${${key}}"
output="${output}$(set_ini_value ${key} ${value})"
done
fi
# Latest element flushing
if [ "${profilePath}" == "${latestPath}" ]; then
write_file "${profileDir}/${LEPTONINFOFILE}" "${output}"
fi
prevDir="${profileDir}"
done
# Verify
check_lepton_ini
lepton_ok_message "Lepton info file created"
}
#** Main ***********************************************************************
install_lepton() {
local profileDir=""
local profileName=""
# Get options.
while getopts 'f:p:h' flag; do
case "${flag}" in
f) profileDir="${OPTARG}" ;;
p) profileName="${OPTARG}" ;;
h)
echo "Lepton Theme Install Script:"
echo " -f <firefox_profile_folder_path>. Set custom Firefox profile folder path."
echo " -p <profile_name>. Set custom profile name."
echo " -h to show this message."
exit 0
;;
esac
done
check_install_types
check_profile_dir "${profileDir}"
check_profile_ini
update_profile_paths
select_profile "${profileName}"
install_profile
write_lepton_info
}
install_lepton "$@"