This commit is contained in:
2026-10-13 12:02:01 +02:00
parent daa2ee755c
commit f931db0808
11 changed files with 117 additions and 34 deletions
+7 -1
View File
@@ -138,7 +138,7 @@ A bar-style prompt showing current time, execution time of the last command
| `rmspc` | filefct | Replace spaces in filenames with underscores (or a custom character) |
| `setc` | lang | Set locale to standard C (POSIX) |
| `setlocale` | lang | Set console locale to any installed locale |
| `settrace` | debug | Activate or deactivate ERR trap to display backtrace on script errors |
| `settrace` | debug | Activate (`--on`), deactivate (`--off`) or query (`--status`) an ERR trap that prints the exit status and a call stack (on stderr) for each failing command; `--force` replaces/removes an ERR trap set by something else; active at startup when `SETTRACE_DEFAULT_ENABLED=1` |
| `showinfo` | info | Display welcome banner and system information (figlet + neofetch/fastfetch) |
| `ssr` | ssh | SSH into a server as root, forwarding extra ssh options |
| `taz` | compress | Compress files and directories into a chosen archive format |
@@ -224,6 +224,12 @@ change the default without having to pass flags every time.
| `UTAZ_DEFAULT_DIR_MODE` | `auto` | Host directory policy: `auto` (only when the archive has no single top-level directory), `always` or `never` |
| `UTAZ_DEFAULT_IGNORE_CASE` | `0` | Set to `1` to match archive extensions case-insensitively (`.ZIP`, `.Tar.GZ`). Per call: `utaz -i` / `utaz -s` |
**`[debug]`** — `settrace`
| Key | Default | Description |
| --- | --- | --- |
| `SETTRACE_DEFAULT_ENABLED` | `0` | Set to `1` to activate the error trace (`settrace --on`) at the startup of every interactive shell |
**`[filefct]`** — file utilities
| Key | Default | Description |
+10
View File
@@ -14,6 +14,8 @@ Versions follow `MAJOR.MINOR.PATCH-REVISION_STAGE_N` (e.g. `3.99.1-4_rc_1`).
- `utaz -i` / `--ignore-case` and `-s` / `--case-sensitive` switches to match
archive extensions case-insensitively or not; the default is set by the new
`UTAZ_DEFAULT_IGNORE_CASE` key of the `[compress]` section (default `0`).
- `SETTRACE_DEFAULT_ENABLED` key in the new `[debug]` configuration options
(default `0`): activates `settrace --on` at startup of interactive shells.
- `utaz` supports bare `.zst` files and `.tzst` archives; `taz -q` is now
effective.
@@ -36,6 +38,14 @@ Versions follow `MAJOR.MINOR.PATCH-REVISION_STAGE_N` (e.g. `3.99.1-4_rc_1`).
(it selected `xterm-direct` even inside tmux/screen or VTE terminals);
multiplexers (tmux, screen) are now checked before emulator hints, since they
inherit the emulator's variables.
- `debug.sh`: the trace is now written to stderr (it used to pollute captured
`$(...)` output) and includes the exit status; `backtrace` and `error` are
exported like `settrace`; `settrace` tells its own trap from a foreign one
(`--status`, and `--off` no longer removes a foreign trap without `--force`);
`--on` when already active is only a warning; `getopt` errors name the command.
- All modules: counters updated with `((x++))`-style commands (which return a
non-zero status when the value was 0) now use plain assignments, so they no
longer trigger the ERR trace or break `set -e`.
- `compress.sh`: option flags and loop variables no longer leak into the
interactive shell.
+5 -1
View File
@@ -56,7 +56,11 @@ PAGER=less
# ==============================================================================
[debug]
# (no configurable keys yet)
# Activate the error trace (settrace --on) at startup of interactive shells:
# every failing command then prints its exit status and a call stack to stderr.
# Verbose; meant for debugging scripts and the profile itself (0=no, 1=yes).
# Can be toggled at any time with settrace --on / --off.
#SETTRACE_DEFAULT_ENABLED=0
# ==============================================================================
[disp]
+6 -1
View File
@@ -42,7 +42,12 @@ TERM=xterm-256color
#UTAZ_DEFAULT_IGNORE_CASE=0
[debug]
# Section used by debug.sh (nothing yet)
# Section used by debug.sh
# Activate the error trace (settrace --on) at startup of interactive shells:
# every failing command then prints its exit status and a call stack to stderr
# (0=no, 1=yes).
#SETTRACE_DEFAULT_ENABLED=0
[disp]
# Section used by disp.sh
+79 -21
View File
@@ -37,48 +37,62 @@
load_conf "debug"
# ------------------------------------------------------------------------------
# Display a backtrace
# Display a backtrace of the current call stack (innermost frame first), one
# line per frame: function, file where it is defined, line of the call site.
# Usage: backtrace
backtrace()
{
printf "========= Call stack =========\n"
local i=1 # We begin at 1 to ignore backtrace itself
while [[ $i -lt ${#FUNCNAME[@]} ]]; do
# BASH_SOURCE[i] is the file defining FUNCNAME[i], and BASH_LINENO[i-1]
# the line of that file from which FUNCNAME[i-1] was called.
printf '%15s() %s:%d\n' \
"${FUNCNAME[$i]}" "${BASH_SOURCE[$i]}" "${BASH_LINENO[$(( i-1 ))]}"
((i++))
i=$(( i + 1 ))
done
unset i
printf "==============================\n"
}
export -f backtrace
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# Function to be trapped for errors investigation
# Handler installed by settrace as ERR trap: report the failure and the call
# stack, then give back the exit status of the failed command so the trap does
# not alter the shell behaviour (e.g. under "set -e").
# Everything goes to stderr: with errtrace a failure inside $(...) or a pipe
# would otherwise inject the trace into the captured output.
# Exported because an exported settrace may be used from child bash processes,
# where the trap needs this function to exist.
error()
{
local errcode=$?
backtrace
printf "Command failed with exit status %d\n" "$errcode" >&2
backtrace >&2
return $errcode
}
export -f error
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# Activate or deactivate error trapping to display backtrace
# Usage: settrace <--on|--off|--status>
# Usage: settrace <--on|--off|--status> [--force]
# Startup behaviour is set by SETTRACE_DEFAULT_ENABLED in the [debug] section.
settrace()
{
local PARSED
PARSED=$(getopt -oh --long help,on,off,status,force -- "$@")
PARSED=$(getopt -oh --long help,on,off,status,force -n settrace -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [[ $? -ne 0 ]]; then
disp E "Invalid options, use \"settrace --help\" to display usage."
return 1
fi
local -a _pargs=()
readarray -td '' _pargs < <(printf '%s' "$PARSED" | xargs printf '%s\0')
set -- "${_pargs[@]}"
# getopt emits a shell-quoted, normalised argument list ("--" ends options)
eval set -- "$PARSED"
# First pass: gather the requested action and flags. --on, --off and
# --status are mutually exclusive; --force only modifies --on.
# --status are mutually exclusive; --force only modifies --on and --off.
local action="" force=0
while true; do
case $1 in
@@ -86,11 +100,14 @@ settrace()
printf "Try to activate backtrace display for script debugging.\n\n"
printf "Options:\n"
printf "\t--on\t\tActivate backtrace generation\n"
printf "\t--force\t\tForce replacement of existing trap (use with --on)\n"
printf "\t--force\t\tReplace (with --on) or remove (with --off) an ERR trap\n"
printf "\t\t\tnot installed by settrace\n"
printf "\t--off\t\tDeactivate backtrace generation\n"
printf "\t--status\tDisplay whether the ERR trap is set\n\n"
printf "That function active a trap event on error. If the script you want to\n"
printf "debug overload the ERR bash trap, it will not work.\n"
printf "That function activates a trap event on error. If the script you want to\n"
printf "debug overloads the ERR bash trap, it will not work.\n\n"
printf "To activate it at every interactive startup, set SETTRACE_DEFAULT_ENABLED=1\n"
printf "in the [debug] section of the profile configuration.\n"
return 0
;;
--on|--off|--status)
@@ -117,25 +134,48 @@ settrace()
done
# Second pass: evaluate the current trap state, then apply the action.
local status="off"
[[ -n $(trap -p ERR) ]] && status="on"
# "trap -p ERR" prints the exact command line that re-creates the trap, so
# comparing it tells our own trap ("on") from one set by someone else
# ("foreign"); empty output means no trap at all ("off").
# Limitation: bash hides the ERR trap from code running in a function (and
# its subshells) unless errtrace is set. Our own trap is always seen since
# settrace sets errtrace with it, but a foreign trap set without errtrace is
# reported as "off" (and can then be replaced without --force).
local status="off" current
current=$(trap -p ERR)
if [[ -n $current ]]; then
status="foreign"
[[ $current == "trap -- 'error' ERR" ]] && status="on"
fi
case $action in
on)
if [[ $status == "on" && $force -eq 0 ]]; then
disp E "ERR signal trap is already set. Use --force to replace it."
if [[ $status == "on" ]]; then
disp W "ERR signal trap is already set."
return 0
fi
if [[ $status == "foreign" && $force -eq 0 ]]; then
disp E "Another ERR signal trap is already set. Use --force to replace it."
return 1
fi
# errtrace makes functions and subshells inherit the ERR trap
set -o errtrace
trap "error" ERR
trap 'error' ERR
;;
off)
[[ $status != "on" ]] && disp W "ERR signal trap is already unset!"
if [[ $status == "foreign" && $force -eq 0 ]]; then
disp E "The ERR signal trap was not set by settrace. Use --force to remove it."
return 1
fi
[[ $status == "off" ]] && disp W "ERR signal trap is already unset!"
trap - ERR
set +o errtrace
;;
status)
disp I "Trap signal on error is ${status}."
case $status in
foreign) disp I "Trap signal on error is set, but not by settrace." ;;
*) disp I "Trap signal on error is ${status}." ;;
esac
;;
"")
disp E "No action specified, use \"settrace --help\" to display usage."
@@ -147,4 +187,22 @@ export -f settrace
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# Optional trace activation at startup (SETTRACE_DEFAULT_ENABLED, default 0).
# Interactive shells only: this file is sourced by non-interactive shells too,
# where an ERR trap would add noise to scripts. [[ $- == *i* ]] is used because
# profile.sh sets INTERACTIVE only after the modules are loaded.
# disp is not defined yet (modules load alphabetically), so the trap is set only
# when none exists, a path that never calls disp. An existing trap is left alone.
# Written as "if" so the sourcing status stays 0 when nothing is done.
case "${SETTRACE_DEFAULT_ENABLED:-0}" in
1|[Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn])
if [[ $- == *i* && -z $(trap -p ERR) ]]; then
settrace --on
fi
;;
esac
# ------------------------------------------------------------------------------
# EOF
+1 -1
View File
@@ -176,7 +176,7 @@ disp()
local _c=${message:_i:1}
_i=$(( _i + 1 ))
line+=$_c
(( line_vis++ ))
(( ++line_vis ))
[[ $_c == ' ' ]] && last_space=${#line}
if (( line_vis > width )); then
if (( last_space > 0 )); then
+2 -2
View File
@@ -234,7 +234,7 @@ pinfo()
[[ "$cpu_model" == "unknown" ]] && cpu_model="${_cpu_line#*: }"
;;
"processor"*)
(( cpu_threads++ ))
cpu_threads=$(( cpu_threads + 1 ))
;;
"physical id"*)
_cur_phys="${_cpu_line#*: }"
@@ -269,7 +269,7 @@ pinfo()
# --- Process count (glob over numeric /proc entries) ---
local proc_count=0 _pdir
for _pdir in /proc/[0-9]*/; do
[[ -d "$_pdir" ]] && (( proc_count++ ))
[[ -d "$_pdir" ]] && proc_count=$(( proc_count + 1 ))
done
# --- Shell and terminal ---
+1 -1
View File
@@ -295,7 +295,7 @@ isipv6()
IFS=':' read -ra parts <<< "$seg"
for part in "${parts[@]}"; do
[[ $part =~ ^[0-9a-fA-F]{1,4}$ ]] || { ok=0; break; }
(( total++ ))
total=$(( total + 1 ))
done
(( ok )) || break
done
+1 -1
View File
@@ -118,7 +118,7 @@ load_theme()
local _lth_line _lth_key _lth_value _lth_varname _lth_lineno=0
while IFS= read -r _lth_line || [[ -n "$_lth_line" ]]; do
((_lth_lineno++))
_lth_lineno=$(( _lth_lineno + 1 ))
_lth_line="${_lth_line%$'\r'}" # strip CR
_lth_line="${_lth_line#"${_lth_line%%[![:space:]]*}"}" # ltrim
_lth_line="${_lth_line%"${_lth_line##*[![:space:]]}"}" # rtrim
+2 -2
View File
@@ -234,7 +234,7 @@ genpwd()
break
fi
((attempts++))
attempts=$(( attempts + 1 ))
done
done
@@ -256,7 +256,7 @@ genpwd()
break
fi
((attempts++))
attempts=$(( attempts + 1 ))
done
done
+3 -3
View File
@@ -300,7 +300,7 @@ _rain_engine()
if ((100 * RANDOM / 32768 < falling_odd)); then
if ((++rains[idx + 1] > term_height)); then
rains=("${rains[@]:0:idx}" "${rains[@]:idx+num_rain_metadata:num_rains*num_rain_metadata}")
((num_rains--))
num_rains=$(( num_rains - 1 ))
continue
fi
fi
@@ -359,7 +359,7 @@ _rain_engine()
X=$((term_width * RANDOM / 32768 + 1))
Y=$((1 - drop_length))
rains=("${rains[@]}" "$X" "$Y" "$rain_drop" "$drop_color" "$drop_length")
((num_rains++))
num_rains=$(( num_rains + 1 ))
fi
do_render
@@ -684,7 +684,7 @@ rainbow()
for ((col = 0; col < term_width; col++)); do
idx=$((col - offset))
while ((idx < 0)); do
((idx += palette_width))
idx=$(( idx + palette_width ))
done
idx=$((idx % palette_width))
row_line+="${RAINBOW_BG_PALETTE[idx]} "