153 lines
5.6 KiB
Bash
153 lines
5.6 KiB
Bash
#!/usr/bin/env bash
|
|
# ------------------------------------------------------------------------------
|
|
# Copyright (c) 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
|
# Protected by the BSD3 license. Please read bellow for details.
|
|
#
|
|
# * Redistribution and use in source and binary forms,
|
|
# * with or without modification, are permitted provided
|
|
# * that the following conditions are met:
|
|
# *
|
|
# * Redistributions of source code must retain the above
|
|
# * copyright notice, this list of conditions and the
|
|
# * following disclaimer.
|
|
# *
|
|
# * Redistributions in binary form must reproduce the above
|
|
# * copyright notice, this list of conditions and the following
|
|
# * disclaimer in the documentation and/or other materials
|
|
# * provided with the distribution.
|
|
# *
|
|
# * Neither the name of the copyright holder nor the names
|
|
# * of any other contributors may be used to endorse or
|
|
# * promote products derived from this software without
|
|
# * specific prior written permission.
|
|
# *
|
|
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
# * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
# * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
# * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
# * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
# * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
# * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
# * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
# * OF SUCH DAMAGE.
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
# ------------------------------------------------------------------------------
|
|
# Helpers shared by several modules. This file is loaded by profile.sh before
|
|
# any module (and by a module sourced alone); the helpers private to one module
|
|
# live in helpers/<module>.sh instead, loaded by the module itself through
|
|
# _profile_load_helpers.
|
|
#
|
|
# Option parsing: the project rule is to never use eval, so _getopt_split and
|
|
# _trap_restore rebuild argument lists and traps in pure bash.
|
|
|
|
# Split the output of getopt into the array whose name is $1, without eval and
|
|
# without xargs. getopt prints the normalised arguments as one shell-quoted
|
|
# line (options bare, every argument in single quotes, a quote inside an
|
|
# argument written '\''), for instance:
|
|
# -s 'a b' --long 'it'\''s' -- 'file' ''
|
|
# Quoted text is taken verbatim (newlines included), an empty argument ('')
|
|
# yields an empty element, and nothing is ever evaluated. The usual call is
|
|
# local -a _pargs=()
|
|
# _getopt_split _pargs "$PARSED"
|
|
# set -- "${_pargs[@]}"
|
|
# Usage: _getopt_split <array_name> <getopt_output>
|
|
_getopt_split()
|
|
{
|
|
local -n _gs_out="$1"
|
|
local s="$2" tok="" in_tok=0 run
|
|
local run_re=$'^[^ \t\n\'\\\\]+'
|
|
_gs_out=()
|
|
|
|
while [[ -n "$s" ]]; do
|
|
# shellcheck disable=SC1003 # '\' is a literal backslash
|
|
case "${s:0:1}" in
|
|
"'")
|
|
s="${s:1}"
|
|
if [[ "$s" == *\'* ]]; then
|
|
tok+="${s%%\'*}"
|
|
s="${s#*\'}"
|
|
else
|
|
# Unterminated quote (never produced by getopt): keep the rest
|
|
tok+="$s"
|
|
s=""
|
|
fi
|
|
in_tok=1
|
|
;;
|
|
'\')
|
|
tok+="${s:1:1}"
|
|
s="${s:2}"
|
|
in_tok=1
|
|
;;
|
|
' '|$'\t'|$'\n')
|
|
(( in_tok )) && _gs_out+=("$tok")
|
|
tok=""
|
|
in_tok=0
|
|
s="${s:1}"
|
|
;;
|
|
*)
|
|
[[ "$s" =~ $run_re ]] || break
|
|
run="${BASH_REMATCH[0]}"
|
|
tok+="$run"
|
|
s="${s:${#run}}"
|
|
in_tok=1
|
|
;;
|
|
esac
|
|
done
|
|
(( in_tok )) && _gs_out+=("$tok")
|
|
return 0
|
|
}
|
|
export -f _getopt_split
|
|
|
|
# Put back traps saved with "trap -p" (one "trap -- 'command' SIGNAL" line per
|
|
# trap), without eval. Used to restore the caller's traps after temporarily
|
|
# replacing them. An empty argument restores nothing.
|
|
# Usage: _trap_restore "$(trap -p INT TERM)"
|
|
_trap_restore()
|
|
{
|
|
local line
|
|
local -a _tr_w=()
|
|
while IFS= read -r line; do
|
|
[[ "$line" == "trap -- "* ]] || continue
|
|
_getopt_split _tr_w "$line"
|
|
# _tr_w = (trap -- <command> <SIGNAL>)
|
|
[[ ${#_tr_w[@]} -eq 4 ]] && trap -- "${_tr_w[2]}" "${_tr_w[3]}"
|
|
done <<< "$1"
|
|
return 0
|
|
}
|
|
export -f _trap_restore
|
|
|
|
# True when $1 is a non-negative integer. Every value that ends up in an
|
|
# arithmetic context must pass this first (this includes values coming from the
|
|
# configuration file): bash evaluates non-numeric strings inside [[ -gt ]] or
|
|
# $(( )) as expressions, which can run arbitrary commands.
|
|
# Usage: _is_uint <value>
|
|
_is_uint()
|
|
{
|
|
[[ "$1" =~ ^[0-9]+$ ]]
|
|
}
|
|
export -f _is_uint
|
|
|
|
# Source the helpers private to a module: helpers/<module name>.sh, found next
|
|
# to the directory holding the module (profile.d/../helpers), the name being the
|
|
# module file name without ".sh". A module without helpers file is fine.
|
|
# Usage: _profile_load_helpers "${BASH_SOURCE[0]}"
|
|
_profile_load_helpers()
|
|
{
|
|
local dir=. name="${1##*/}"
|
|
[[ "$1" == */* ]] && dir="${1%/*}"
|
|
name="$dir/../helpers/${name%.sh}.sh"
|
|
[[ -r "$name" ]] || return 0
|
|
# shellcheck source=/dev/null
|
|
. "$name" || printf "[ Warning ] Failed to source helpers: %s\n" "$name" >&2
|
|
}
|
|
export -f _profile_load_helpers
|
|
|
|
# ------------------------------------------------------------------------------
|
|
|
|
|
|
# EOF
|