blob: 9360fadffc354365b47c8e28e4add7c53fe01ffa [file] [log] [blame] [edit]
#!/bin/bash
###############################################################################
# BRLTTY - A background process providing access to the console screen (when in
# text mode) for a blind person using a refreshable braille display.
#
# Copyright (C) 1995-2023 by The BRLTTY Developers.
#
# BRLTTY comes with ABSOLUTELY NO WARRANTY.
#
# This is free software, placed under the terms of the
# GNU Lesser General Public License, as published by the Free Software
# Foundation; either version 2.1 of the License, or (at your option) any
# later version. Please see the file LICENSE-LGPL for details.
#
# Web Page: http://brltty.app/
#
# This software is maintained by Dave Mielke <dave@mielke.cc>.
###############################################################################
. "$(dirname "${BASH_SOURCE[0]}")/brltty-prologue.sh"
getElement() {
eval setVariable "\${1}" "\"\${${2}[\"${3}\"]}\""
}
setElement() {
eval setVariable "\${1}[\"${2}\"]" "\"\${3}\""
}
setElements() {
local _array="${1}"
shift 1
eval "${_array}=(\"\${@}\")"
}
appendElements() {
local _array="${1}"
shift 1
eval "${_array}+=(\"\${@}\")"
}
prependElements() {
local _array="${1}"
shift 1
eval setElements "\${_array}" "\"\${@}\" \"\${${_array}[@]}\""
}
shiftElements() {
local _array="${1}"
local _count="${2}"
eval set -- "\"\${${_array}[@]}\""
shift "${_count}"
setElements "${_array}" "${@}"
}
getElementCount() {
eval setVariable "\${1}" "\${#${2}[*]}"
}
getElementNames() {
eval "${1}=(\"\${!${2}[@]}\")"
}
forElements() {
local _array="${1}"
shift 1
local _names=()
getElementNames _names "${_array}"
local name
for name in "${_names[@]}"
do
local value
getElement value "${_array}" "${name}"
"${@}" "${name}" "${value}"
done
}
getElements() {
local _toArray="${1}"
local _fromArray="${2}"
forElements "${_fromArray}" setElement "${_toArray}"
}
listElement() {
local array="${1}"
local name="${2}"
local value="${3}"
echo "${array}[${name}]: ${value}"
}
listElements() {
local _array="${1}"
forElements "${_array}" listElement "${_array}" | sort
}
writeElement() {
local name="${1}"
local value="${2}"
echo "${name} ${value}"
}
writeElements() {
local _array="${1}"
forElements "${_array}" writeElement | sort
}
readElements() {
local _array="${1}"
local name value
while read name value
do
setElement "${_array}" "${name}" "${value}"
done
}
toRelativePath() {
local toPath="${1}"
local variable="${2}"
[ -n "${toPath}" ] || toPath="."
resolveDirectory "${toPath}" toPath
local fromPath="$(pwd)"
[ "${fromPath%/}" = "${fromPath}" ] && fromPath+="/"
[ "${toPath%/}" = "${toPath}" ] && toPath+="/"
local fromLength="${#fromPath}"
local toLength="${#toPath}"
local limit=$(( (fromLength < toLength)? fromLength: toLength ))
local index=0
local start=0
while (( index < limit ))
do
local fromChar="${fromPath:index:1}"
local toChar="${toPath:index:1}"
[ "${fromChar}" = "${toChar}" ] || break
[ "${fromChar}" = "/" ] && start="$((index + 1))"
let "index += 1"
done
fromPath="${fromPath:start}"
toPath="${toPath:start}"
while [ "${#fromPath}" -gt 0 ]
do
toPath="../${toPath}"
fromPath="${fromPath#*/}"
done
[ -n "${toPath}" ] || toPath="."
if [ -n "${variable}" ]
then
setVariable "${variable}" "${toPath}"
else
echo "${toPath}"
fi
}
programConfigurationFilePrefixArray=()
makeProgramConfigurationFilePrefixArray() {
[ "${#programConfigurationFilePrefixArray[*]}" -gt 0 ] || {
programConfigurationFilePrefixArray+=("${programDirectory}/.")
[ -n "${HOME}" ] && {
programConfigurationFilePrefixArray+=("${HOME}/.config/${programName}/")
programConfigurationFilePrefixArray+=("${HOME}/.")
}
programConfigurationFilePrefixArray+=("/etc/${programName}/")
programConfigurationFilePrefixArray+=("/etc/xdg/${programName}/")
programConfigurationFilePrefixArray+=("/etc/")
}
}
findProgramConfigurationFile() {
local fileVariable="${1}"
shift 1
[ "${#}" -gt 0 ] || set -- conf cfg
local fileExtensions=("${@}")
makeProgramConfigurationFilePrefixArray
local prefix
for prefix in "${programConfigurationFilePrefixArray[@]}"
do
local extension
for extension in "${fileExtensions[@]}"
do
local _file="${prefix}${programName}.${extension}"
[ -f "${_file}" ] || continue
[ -r "${_file}" ] || continue
setVariable "${fileVariable}" "${_file}"
return 0
done
done
return 1
}
programComponentDirectoryArray=()
makeProgramComponentDirectoryArray() {
[ "${#programComponentDirectoryArray[*]}" -gt 0 ] || {
local subdirectory="libexec"
programComponentDirectoryArray+=("${PWD}")
programComponentDirectoryArray+=("${PWD}/../${subdirectory}")
programComponentDirectoryArray+=("${programDirectory}")
programComponentDirectoryArray+=("${programDirectory}/../${subdirectory}")
[ -n "${HOME}" ] && {
programComponentDirectoryArray+=("${HOME}/.config/${programName}")
programComponentDirectoryArray+=("${HOME}/${subdirectory}")
}
programComponentDirectoryArray+=("/usr/${subdirectory}")
programComponentDirectoryArray+=("/${subdirectory}")
programComponentDirectoryArray+=("/etc/${programName}")
programComponentDirectoryArray+=("/etc/xdg/${programName}")
}
}
findProgramComponent() {
local fileVariable="${1}"
local name="${2}"
shift 2
local fileExtensions=("${@}")
if [ "${#fileExtensions[*]}" -eq 0 ]
then
fileExtensions=(bash sh)
else
name="${programName}.${name}"
fi
makeProgramComponentDirectoryArray
local directory
for directory in "${programComponentDirectoryArray[@]}"
do
local extension
for extension in "${fileExtensions[@]}"
do
local _file="${directory}/${name}.${extension}"
[ -f "${_file}" ] || continue
[ -r "${_file}" ] || continue
setVariable "${fileVariable}" "${_file}"
return 0
done
done
return 1
}
includeProgramComponent() {
local name="${1}"
local component
findProgramComponent component "${name}" || {
logWarning "program component not found: ${name}"
return 1
}
. "${component}" || {
logWarning "problem including program component: ${component}"
return 2
}
logNote "program component included: ${component}"
return 0
}
declare -A persistentProgramSettingsArray=()
persistentProgramSettingsChanged=false
readonly persistentProgramSettingsExtension="conf"
restorePersistentProgramSettins() {
local settingsFile="${1}"
local found=false
if [ -n "${settingsFile}" ]
then
[ -e "${settingsFile}" ] && found=true
elif findProgramConfigurationFile settingsFile "${persistentProgramSettingsExtension}"
then
found=true
fi
"${found}" && {
logNote "restoring persistent program settings: ${settingsFile}"
persistentProgramSettingsArray=()
readElements persistentProgramSettingsArray <"${settingsFile}"
persistentProgramSettingsChanged=false
}
}
savePersistentProgramSettins() {
local settingsFile="${1}"
"${persistentProgramSettingsChanged}" && {
[ -n "${settingsFile}" ] || {
findProgramConfigurationFile settingsFile "${persistentProgramSettingsExtension}" && [ -f "${settingsFile}" -a -w "${settingsFile}" ] || {
settingsFile=""
local prefix
for prefix in "${programConfigurationFilePrefixArray[@]}"
do
local directory="${prefix%/}"
[ "${directory}" = "${prefix}" ] && continue
if [ -e "${directory}" ]
then
[ -d "${directory}" ] || continue
else
mkdir --parents -- "${directory}" || continue
logNote "program configuration directory created: ${directory}"
fi
[ -w "${directory}" ] || continue
settingsFile="${directory}/${programName}.${persistentProgramSettingsExtension}"
break
done
[ -n "${settingsFile}" ] || {
logWarning "no eligible program configuration directory"
return 1
}
}
}
logNote "saving persistent program settings: ${settingsFile}"
writeElements persistentProgramSettingsArray >"${settingsFile}"
persistentProgramSettingsChanged=false
}
return 0
}
getPersistentProgramSetting() {
local variable="${1}"
local name="${2}"
setVariable "${variable}" "${persistentProgramSettingsArray["${name}"]}"
}
changePersistentProgramSetting() {
local name="${1}"
local value="${2}"
local -n setting="persistentProgramSettingsArray[\"${name}\"]"
[ "${value}" = "${setting}" ] || {
setting="${value}"
persistentProgramSettingsChanged=true
}
}
evaluateExpression() {
local resultVariable="${1}"
local expression="${2}"
local _result
_result="$(bc --quiet <<<"${expression}")" || return 1
[ -n "${_result}" ] || return 1
setVariable "${resultVariable}" "${_result}"
return 0
}
if [ -n "${SRANDOM}" ]
then
declare -n bashRandomNumberVariable="SRANDOM"
else
declare -n bashRandomNumberVariable="RANDOM"
fi
getRandomInteger() {
local resultVariable="${1}"
local maximumValue="${2}"
local minimumValue="${3:-1}"
setVariable "${resultVariable}" $(( (bashRandomNumberVariable % (maximumValue - minimumValue + 1)) + minimumValue ))
}
convertUnit() {
local resultVariable="${1}"
local from="${2}"
local to="${3}"
local precision="${4}"
local toValue
toValue="$(units --terse --output-format "%.${precision:-0}f" "${from}" "${to}")" || return 1
[ "${toValue}" = "${toValue%.*}" ] || {
toValue="${toValue%%*(0)}"
toValue="${toValue%.}"
}
setVariable "${resultVariable}" "${toValue}"
return 0
}
convertSimpleUnit() {
local resultVariable="${1}"
local fromValue="${2}"
local fromUnit="${3}"
local toUnit="${4}"
local precision="${5}"
convertUnit "${resultVariable}" "${fromValue}${fromUnit}" "${toUnit}" "${precision}" || return 1
return 0
}
formatSimpleUnit() {
local variable="${1}"
local fromUnit="${2}"
local toUnit="${3}"
local precision="${4}"
local value="${!variable}"
convertSimpleUnit value "${value}" "${fromUnit}" "${toUnit}" "${precision}" || return 1
setVariable "${variable}" "${value}${toUnit}"
return 0
}
convertComplexUnit() {
local resultVariable="${1}"
local fromValue="${2}"
local fromUnit="${3}"
local toUnit="${4}"
local unitType="${5}"
local precision="${6}"
convertUnit "${resultVariable}" "${unitType}${fromUnit}(${fromValue})" "${unitType}${toUnit}" "${precision}" || return 1
return 0
}
formatComplexUnit() {
local variable="${1}"
local fromUnit="${2}"
local toUnit="${3}"
local unitType="${4}"
local precision="${5}"
local value="${!variable}"
convertComplexUnit value "${value}" "${fromUnit}" "${toUnit}" "${unitType}" "${precision}" || return 1
setVariable "${variable}" "${value}${toUnit}"
return 0
}
isAbbreviation() {
local abbreviation="${1}"
shift 1
local length="${#abbreviation}"
local word
for word
do
[ "${length}" -le "${#word}" ] || continue
[ "${abbreviation}" = "${word:0:length}" ] && return 0
done
return 1
}
verifyChoice() {
local label="${1}"
local valueVariable="${2}"
shift 2
local value
getVariable "${valueVariable}" value
local candidates=()
local choice
for choice
do
[ "${value}" = "${choice}" ] && return 0
isAbbreviation "${value}" "${choice}" || continue
candidates+=("${choice}")
done
local count="${#candidates[*]}"
[ "${count}" -gt 0 ] || syntaxError "invalid ${label}: ${value}"
[ "${count}" -eq 1 ] || {
local message="ambiguous ${label}: ${value}"
local delimiter=" ("
for choice in "${candidates[@]}"
do
message+="${delimiter}${choice}"
delimiter=", "
done
message+=")"
syntaxError "${message}"
}
setVariable "${valueVariable}" "${candidates[0]}"
}
confirmAction() {
local prompt="${1}"
local noWord="no"
local yesWord="yes"
local response
while read -p "${programName}: ${prompt} ([${noWord}] | ${yesWord})? " -r response
do
[ -n "${response}" ] || return 1
response="${response,,*}"
isAbbreviation "${response}" "${noWord}" && return 1 ||
isAbbreviation "${response}" "${yesWord}" && return 0 ||
programMessage "unrecognized response"
done
echo >&2 ""
return 2
}