# ------------------------------------------------------------------------------
# nf.sh
# =====
#
# Project Library
# Scope macOS
# Copyright (C) 2024 by RaySoft, Zurich, Switzerland
# License GNU General Public License (GPL) 2.0
# https://www.gnu.org/licenses/gpl2.txt
#
# ------------------------------------------------------------------------------
# Exits if the library is run as a script and not sourced as a library
if [[ "${BASH_SOURCE[0]}" == "$0" ]]; then
echo "Don't run this script! Just source it."
exit 1
fi
# Returns if the library is already loaded
if declare -F 'nf' >'/dev/null' 2>&1; then
return 0
fi
# ------------------------------------------------------------------------------
# The prompt printed before the command line is echoed when the -x option is set.
PS4='\n$(nf::runtime_position 0): '
# ------------------------------------------------------------------------------
nf() {
# Shows all function of this library.
#
# Arguments:
# none
#
# Returns:
# 0: Success
# >0: Error
nf::print_heading '-' 'ba' "My 'Notification' Library"
# Prints all functions starting with the prefix 'xx::' but ignore private
# functions with the prefix 'xx::_'
compgen -c "${FUNCNAME[0]}::" | grep -v "${FUNCNAME[0]}::_"
echo
return 0
}
# ------------------------------------------------------------------------------
nf::carp() {
# Shows the exact position of an error, prints the error message and
# optionally exits the script.
# The name of this function was derived from the Perl module "Carp".
#
# Arguments:
# $1: Parameter '-e' with error number (optional)
# $@: Error message
#
# Returns:
# 0: Success
# >0: Error
#
# Example:
# nf::carp -e2 'Error running AppleScript!'
# Print the runtime position of the script
if ! nf::runtime_position 1; then
return 1
fi
# If the first argument starts with '-e' followed by a number
if [[ "$1" =~ ^-e([0-9]*)$ ]]; then
# Prints the error message
echo ": ${*:2}"
# Exits the script
exit "${BASH_REMATCH[1]:-1}"
else
# Prints the error message
echo ": $*"
fi
return 0
} 1>&2
# ------------------------------------------------------------------------------
nf::notify_macos() {
# Sends a notification to the macOS notification center.
#
# Arguments:
# $1: Title of the message
# $2: Sub title of the message (optional)
# $@: Message (optional)
#
# Returns:
# 0: Success
# >0: Error
#
# Example:
# nf::notify_macos 'Backup' 'Backup ended successfully!' "$(date '+%F %H:%M')"
# Returns if the function was called incorrectly
if [[ "$#" -eq 0 ]]; then
nf::carp 'Error calling function!'
return 1
fi
# Returns if a needed binary is not available
for bin in 'osascript'; do
if ! type -P "${bin}" >'/dev/null' 2>&1; then
nf::carp "Error finding binary: ${bin}!"
return 1
fi
done
# Defines a local variable to put together the AppleScript
local script="display notification"
# Adds the message if it is available
if [[ "$#" -gt 2 ]]; then
script+=" \"${*:3}\""
fi
# Adds the title
script+=" with title \"$1\""
# Adds the subtitle if it is available
if [[ "$#" -gt 1 ]]; then
script+=" subtitle \"$2\""
fi
# Runs the AppleScript
if ! osascript -e "${script}" >'/dev/null' 2>&1; then
nf::carp 'Error running AppleScript!'
return 1
fi
return 0
}
# ------------------------------------------------------------------------------
nf::print_heading() {
# Prints a heading.
#
# Arguments:
# $1: Type of underline ('-', '=' or '+')
# $2: New line before (b) or after (a) the heading. '-' for no new lines.
# $@: Message
#
# Returns:
# 0: Success
# >0: Error
#
# Example:
# nf::print_heading '=' 'ba' 'Heading'
# Returns if the function was called incorrectly
if [[ "$#" -lt 3 || ! "$1" =~ ^[-=+]$ || ! "$2" =~ ^(-|[ab]{1,2})$ ]]; then
nf::carp 'Error calling function!'
return 1
fi
# Prints a newline if the second argument ($2) contains a 'b'
[[ "$2" =~ b ]] && echo
# Prints the heading message and underlines it with character provides in the
# first argument ($1)
if ! sed "p; s/./$1/g" <<<"${*:3}" 2>'/dev/null'; then
nf::carp 'Error printing heading!'
return 1
fi
# Prints a newline if the second argument ($2) contains a 'a'
[[ "$2" =~ a ]] && echo
return 0
}
# ------------------------------------------------------------------------------
nf::runtime_position() {
# Print the runtime position of a script.
#
# Arguments:
# $1: Number of elements to be ignored at the end of the 'FUNCNAME' array
#
# Returns:
# String
# Returns if the function was called incorrectly
if [[ "$#" -ne 1 || ! "$1" =~ ^[0-9]+$ ]]; then
echo 'Error calling function!'
return 1
fi
# Defines local variables
local i=0
# For all elements in the array 'FUNCNAME' in reverse order
for ((i = "${#FUNCNAME[@]}" - 1; i > "$1"; i--)); do
# If the function name is 'main' or 'source'
if [[ "${FUNCNAME[$i]}" =~ ^main|source$ ]]; then
# Prints the script name
echo -n "${BASH_SOURCE[$i]##*/}"
else
# Prints the function name
echo -n "${FUNCNAME[$i]}"
fi
# Prints the corresponding line number
echo -n "[${BASH_LINENO[$((i - 1))]}]"
# Prints a separator if it is not the last iteration
[[ "$i" -gt $(( "$1" + 1 )) ]] && echo -n '>'
done
return 0
}
# ------------------------------------------------------------------------------
nf::tail() {
# Opens a new iTerm window and tails a file in it.
#
# Arguments:
# $1: File
#
# Returns:
# 0: Success
# >0: Error
#
# Example:
# nf::tail '/var/log/system.log'
# Returns if the function was called incorrectly
if [[ "$#" -ne 1 ]]; then
nf::carp 'Error calling function!'
return 1
fi
local bin=''
# Returns if a needed binary is not available
for bin in 'osascript'; do
if ! type -P "${bin}" >'/dev/null' 2>&1; then
nf::carp "Error finding binary: ${bin}!"
return 1
fi
done
# Returns if the file is not available
if [[ ! -f "$1" ]]; then
nf::carp "Error finding file: $1!"
return 1
fi
# Runs the AppleScript to open a new iTerm windows and executes the 'tail'
# command in it
if ! osascript <<-EOS >'/dev/null' 2>&1; then
tell application "iTerm"
set newWindow to (create window with default profile)
tell newWindow
tell current session of newWindow to write text "tail -f -n '+1' '$1'"
end tell
end tell
EOS
nf::carp 'Error running AppleScript!'
return 1
fi
return 0
}
# ------------------------------------------------------------------------------
return 0
# Path to the global libraries
XX_DE_GLOBAL_LIB="${HOME}/dev/lib"
# Global libraries to be loaded
XX_GLOBAL_LIBS=('nf')
# Test if library path is available
if [[ ! -d "${XX_DE_GLOBAL_LIB}" ]]; then
echo "Error finding directory: ${XX_DE_GLOBAL_LIB}!"
exit 1
fi
# Load libraries
for lib in "${XX_GLOBAL_LIBS[@]}"; do
lib="${XX_DE_GLOBAL_LIB}/${lib}.sh"
if [[ ! -f "${lib}" ]]; then
echo "Error finding library: ${lib}!"
exit 1
fi
if ! source "${lib}"; then
exit 1
fi
done