#!/usr/bin/env bash # ------------------------------------------------------------------------------ # Copyright (c) 2013-2026 Geoffray Levasseur # 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/.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 _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 -- ) [[ ${#_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 _is_uint() { [[ "$1" =~ ^[0-9]+$ ]] } export -f _is_uint # Source the helpers private to a module: helpers/.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