#!/bin/bash -e # # Towel: Randomly Useful Bash Functions # Never Leave Home Without Your Towel # # Copyright (C) 2019 Scott Duensing # # This software is provided 'as-is', without any express or implied # warranty. In no event will the authors be held liable for any damages # arising from the use of this software. # # Permission is granted to anyone to use this software for any purpose, # including commercial applications, and to alter it and redistribute it # freely, subject to the following restrictions: # # 1. The origin of this software must not be misrepresented; you must not # claim that you wrote the original software. If you use this software # in a product, an acknowledgment in the product documentation would be # appreciated but is not required. # 2. Altered source versions must be plainly marked as such, and must not be # misrepresented as being the original software. # 3. This notice may not be removed or altered from any source distribution. # # # Color constants for tBoldBox. # tRED=1 tGREEN=2 tYELLOW=3 tBLUE=4 tPURPLE=5 tCYAN=6 # # Global variables used by Towel. # __tG__PARENTPATH="${BASH_SOURCE[0]}" __tG__SUDOPASS= # # Argument helper to process things intended for Towel. # # Parameters: # ARGS - All command line arguments passed to script. # function tArgsHandler() { local __FIRST=$1 # Is SUDO after us? if [[ "${__FIRST}" == "TOWEL_ASKPASS" ]]; then tSudoAskPass # Does not return. fi } # # Draws bold white text inside a colored box. # # Parameters: # # COLOR - Towel color constant for box background color. # TEXT - Text to display. # function tBoldBox() { local __COLOR=$1 local __TEXT=$2 local __LEN=$((${#__TEXT}+6)) local __SPACES=$(printf "%${__LEN}s") __COLOR=${!__COLOR} echo -e "\e[1m\e[4${__COLOR}m${__SPACES}\e[0m" echo -e "\e[1m\e[4${__COLOR}m ${__TEXT} \e[0m" echo -e "\e[1m\e[4${__COLOR}m${__SPACES}\e[0m" } # # Checks to see if listed packages have been installed. # Currently DEB packages only. # # Parameters: # # RESULT - Variable in which to return the packages that are missing. # VAR - List of packages to check, sparated by spaces. # function tCheckPackages() { local __RESULT=$1 local __PACKAGES=${*:2} local __MISSING= local __P= set +e for __P in ${__PACKAGES}; do dpkg-query -s ${__P} 2> /dev/null | grep -q ^"Status: install ok installed"$ if [ "$?x" == "1x" ]; then __MISSING="${__MISSING} ${__P}" fi done set -e tTrimL __MISSING "${__MISSING}" eval $__RESULT=\${__MISSING} } # # Downloads file. # # Parameters: # # URL - URL of file to download. # FILE - Optional alternate name to save downloaded file under. # function tDownload() { local __URL=$1 local __FILE= tTrim __FILE "$2" if [[ -z ${__FILE} ]]; then __FILE=${__URL##*/} fi if [[ -e "${__FILE}" ]]; then rm -f "${__FILE}" fi wget -O "${__FILE}" ${__URL} } # # Escapes a given string so it's safe to write into an XML document. # # Parameters: # # RESULT - Variable in which to return the escaped string. # VAR - String to escape. # function tEscapeXml() { local __RESULT=$1 local __VAR=${*:2} __VAR=$(echo ${__VAR} | sed -e 's~&~\&~g' -e 's~<~\<~g' -e 's~>~\>~g') eval $__RESULT=\${__VAR} } # # Downloads the latest release of a project from GitHub. # # Parameters: # # RESULT - Variable in which to return the name of the downloaded file. # DEVELOPER - GitHub developer name that owns the project. # PROJECT - Which project from that developer to download. # EXTENSION - Which file extension to download from the release list. # function tFetchGitHubRelease() { local __RESULT=$1 local __DEVELOPER=$2 local __PROJECT=$3 local __EXTENSION=$4 local __URL=$(curl -s https://api.github.com/repos/${__DEVELOPER}/${__PROJECT}/releases \ | grep "browser_download_url.*${__EXTENSION}" \ | cut -d : -f 2,3 \ | tr -d \" \ | head -n 1) local __FILE=${__URL##*/} tDownload ${__URL} eval $__RESULT=\${__FILE} } # # Displays a whiptail-based file browser for selecting a particular type of file. # # Parameters: # # RESULT - Variable in which to store selected filename, or empty if dialog is canceled. # TITLE - Title to display in the file browser window. # EXTENSION - The extension to allow to be selected (in ".ext" format). # START - Optional. Path to begin browsing from. # function tFileBrowser() { local __RESULT=$1 local __TITLE=$2 local __EXTENSION=$3 local __START=$4 local __SELECTION= local __DIRLIST=() local __CURDIR= local __RET= local __LOOPING=1 pushd . > /dev/null if [[ ! -z ${__START} ]]; then cd "${__START}" fi while [[ ${__LOOPING} -eq 1 ]]; do __DIRLIST=() if [[ "${__CURDIR}" != "/" ]]; then __DIRLIST[${#__DIRLIST[@]}+1]=".." __DIRLIST[${#__DIRLIST[@]}+1]="Up" fi for F in *; do if [[ -d "${F}" ]]; then __DIRLIST[${#__DIRLIST[@]}+1]=$(echo "${F}/ ") else __DIRLIST[${#__DIRLIST[@]}+1]=$(echo "${F} ") fi __DIRLIST[${#__DIRLIST[@]}+1]=$(ls -lah "${F}" | awk '{ print $5 }') done __CURDIR=$(pwd) set +e __SELECTION=$(whiptail --title "${__TITLE}" \ --menu "PgUp/PgDn/Arrows ENTER Selects File/Folder or TAB Key\n\n${__CURDIR}" 0 0 0 \ --cancel-button "Cancel" \ --ok-button "Select" "${__DIRLIST[@]}" 3>&1 1>&2 2>&3) __RET=$? set -e if [[ ${__RET} -eq 1 ]]; then # User Selected Cancel __SELECTION= __LOOPING=0 elif [[ ${__RET} -eq 0 ]]; then tTrim __SELECTION "${__SELECTION}" if [[ -d "${__SELECTION}" ]]; then # Was a directory selected? cd "${__SELECTION}" elif [[ -f "${__SELECTION}" ]]; then # Was a file selected? if [[ ${__SELECTION} == *${__EXTENSION} ]]; then # Check if file has given extension if (whiptail --title "Confirm Selection" \ --yesno "Directory: ${__CURDIR}\n Filename: ${__SELECTION}" 0 0 \ --yes-button "Confirm" \ --no-button "Reselect"); then __SELECTION="$(pwd)/${__SELECTION}" __LOOPING=0 fi else # Not requested extension whiptail --title "Error" \ --msgbox "Not a ${__EXTENSION} file" 0 0 fi else # Not a file or folder whiptail --title "Error" \ --msgbox "Unable to change to directory ${__SELECTION}" 0 0 fi fi done eval $__RESULT=\${__SELECTION} popd > /dev/null } # # Returns the absolute path of a given file. # # Parameters: # # RESULT - Variable in which to return the absolute path. # SOURCE - File which we wish to find the absolute path. # function tFindPath() { local __RESULT=$1 local __SOURCE=$2 local __DIR= while [[ -h "${__SOURCE}" ]]; do __DIR="$( cd -P "$( dirname "${__SOURCE}" )" && pwd )" __SOURCE="$(readlink "${__SOURCE}")" # If $__SOURCE was a relative symlink, we need to resolve it # relative to the path where the symlink file was located. [[ ${__SOURCE} != /* ]] && __SOURCE="${__DIR}/${__SOURCE}" done __DIR="$( cd -P "$( dirname "${__SOURCE}" )" && pwd )" eval $__RESULT=\${__DIR} } # # Returns the location and name of the parent script that included us. # # Parameters: # # RESULT - Variable in which we want the absolute filename returned. # function tGetParentPath() { local __RESULT=$1 local __PARENTPATH= # We have to initialize __tG__PARENTPATH before the calling script # changes directories elsewhere. We set it on load. tFindPath __PARENTPATH "${__tG__PARENTPATH}" __TOWELPATH="${__PARENTPATH}/`basename \"${__tG__PARENTPATH}\"`" eval $__RESULT=\${__PARENTPATH} } # # Returns the location and name of this Towel script. # # Parameters: # # RESULT - Variable in which we want the absolute filename returned. # function tGetTowelPath() { local __RESULT=$1 local __TOWELPATH= tFindPath __TOWELPATH "${BASH_SOURCE[0]}" __TOWELPATH="${__TOWELPATH}/`basename \"${BASH_SOURCE[0]}\"`" eval $__RESULT=\${__TOWELPATH} } # # Runs program with sudo asking for password using a nice dialog box. # # Parameters: # # ARGS - Command to execute with sudo. # function tSudo() { local __ARGS="$@" local __ASK="$(mktemp --suffix=.sh -q)" if [[ "${__tG__SUDOPASS}x" == "x" ]]; then cat <<- ASKPASS > "${__ASK}" #!/bin/bash "${BASH_SOURCE[1]}" TOWEL_ASKPASS ASKPASS else cat <<- ASKPASS > "${__ASK}" #!/bin/bash echo "${__tG__SUDOPASS}" ASKPASS fi chmod +x "${__ASK}" if [[ "${__ARGS}x" == "x" ]]; then SUDO_ASKPASS="${__ASK}" sudo -A -v else SUDO_ASKPASS="${__ASK}" sudo -A ${__ARGS} fi rm "${__ASK}" } # # Helper function for asking for password using a nice dialog box. # # Parameters: # # None. # function tSudoAskPass() { local __RET= local __PASSWORD= set +e __PASSWORD=$( whiptail --passwordbox "\n$(basename "${BASH_SOURCE[1]}") needs superuser access.\n\nPlease enter your password:" 12 60 --title "Need Root" 3>&1 1>&2 2>&3 ) __RET=$? set -e if [[ ${__RET} -ne 0 ]]; then echo "Cannot continue without superuser access." 1>&2 exit 1 fi printf "${__PASSWORD}\n" exit 0 } # # Allows specifying the sudo password for automated installs. # # Parameters: # # PASSWORD - The sudo password to use. # function tSudoSetPassword() { local __PASSWORD=$1 __tG__SUDOPASS=${__PASSWORD} } # # Removes whitespace from the beginning and end of a string. # # Parameters: # # RESULT - Variable in which to return the trimmed string. # VAR - String to trim. # function tTrim() { local __RESULT=$1 local __VAR="${*:2}" # remove leading whitespace characters __VAR="${__VAR#"${__VAR%%[![:space:]]*}"}" # remove trailing whitespace characters __VAR="${__VAR%"${__VAR##*[![:space:]]}"}" eval $__RESULT=\${__VAR} } # # Removes whitespace from the beginning of a string. # # Parameters: # # RESULT - Variable in which to return the trimmed string. # VAR - String to trim. # function tTrimL() { local __RESULT=$1 local __VAR="${*:2}" # remove leading whitespace characters __VAR="${__VAR#"${__VAR%%[![:space:]]*}"}" eval $__RESULT=\${__VAR} } # # Removes whitespace from the end of a string. # # Parameters: # # RESULT - Variable in which to return the trimmed string. # VAR - String to trim. # function tTrimR() { local __RESULT=$1 local __VAR="${*:2}" # remove trailing whitespace characters __VAR="${__VAR%"${__VAR##*[![:space:]]}"}" eval $__RESULT=\${__VAR} } # # We're a library. Don't run us. # if [ -z "${BASH_SOURCE[1]}" ]; then echo "This is a library. Include it into other scripts. Don't execute it." echo "Visit https://skunkworks.kangaroopunch.com/skunkworks/towel for more." exit 0 fi