fix conf management
This commit is contained in:
@@ -94,8 +94,8 @@ A bar-style prompt showing current time, execution time of the last command
|
|||||||
| `busy` | fun | Monitor /dev/urandom for a hex pattern — look busy |
|
| `busy` | fun | Monitor /dev/urandom for a hex pattern — look busy |
|
||||||
| `check_updates` | updates | Check whether a newer profile version is available online; when called with `-q` at startup a 3-second network timeout is applied so a slow or absent network never delays the prompt |
|
| `check_updates` | updates | Check whether a newer profile version is available online; when called with `-q` at startup a 3-second network timeout is applied so a slow or absent network never delays the prompt |
|
||||||
| `clean` | filefct | Erase backup files in given directories, optionally recursive |
|
| `clean` | filefct | Erase backup files in given directories, optionally recursive |
|
||||||
| `conf_dump` | conf | Display the profile configuration file; `-s/--section NAME` restricts output to one section; an optional pattern argument filters keys by substring match |
|
| `conf_dump` | conf | Display the profile configuration file; `-s/--section NAME` restricts output to one section; `-a/--all` also lists commented-out keys (documented defaults, skipped when the key is set); an optional pattern argument filters keys by substring match; honours `NO_COLOR` |
|
||||||
| `conf_save` | conf | Save or update a key=value pair in a configuration section: `conf_save <section> <key> <value>`; creates the section header automatically if absent |
|
| `conf_save` | conf | Save or update a key=value pair in a configuration section: `conf_save <section> <key> <value>`; replaces an existing key in place or appends it to the section (creating the section if absent); works through a symlinked file and keeps its permissions; the value must be single-line and the running shell is not updated (applies at next load) |
|
||||||
| `disp` | disp | Display formatted info / warning / error / debug messages; long messages are word-wrapped and continuation lines are indented to align with the message text |
|
| `disp` | disp | Display formatted info / warning / error / debug messages; long messages are word-wrapped and continuation lines are indented to align with the message text |
|
||||||
| `dwl` | net | Download a URL using curl, wget, or fetch transparently; supports `-t <seconds>` / `--timeout <seconds>` to cap the transfer time |
|
| `dwl` | net | Download a URL using curl, wget, or fetch transparently; supports `-t <seconds>` / `--timeout <seconds>` to cap the transfer time |
|
||||||
| `expandlist` | filefct | Expand glob expressions into a quoted, separated list |
|
| `expandlist` | filefct | Expand glob expressions into a quoted, separated list |
|
||||||
@@ -188,21 +188,23 @@ automatically at startup:
|
|||||||
| *(absent or `smart`)* | Auto-detect the best available `terminfo` entry (default) |
|
| *(absent or `smart`)* | Auto-detect the best available `terminfo` entry (default) |
|
||||||
| any other string | Exported as-is — standard Unix behaviour |
|
| any other string | Exported as-is — standard Unix behaviour |
|
||||||
|
|
||||||
When auto-detection runs, `COLORTERM=truecolor|24bit` is first evaluated as a
|
When auto-detection runs, `COLORTERM=truecolor|24bit` is evaluated as a global
|
||||||
global truecolor flag. Each emulator hint is then checked in order; within each
|
truecolor flag: it does not pick an entry by itself, it only enables the
|
||||||
branch a `*-direct` terminfo entry (24-bit colour) is tried before the
|
`*-direct` (24-bit colour) terminfo entries below, each of which falls back to its
|
||||||
`*-256color` fallback. Only entries confirmed to exist via `terminfo` are used.
|
`*-256color` counterpart. Hints are checked in the following order and the first
|
||||||
|
entry confirmed to exist via `terminfo` is used. Terminal multiplexers come first
|
||||||
|
because they inherit the outer emulator's variables (`TERM_PROGRAM`,
|
||||||
|
`VTE_VERSION`, …) but need their own `TERM`.
|
||||||
|
|
||||||
1. **`$COLORTERM`** — `truecolor` or `24bit` sets the truecolor flag for all subsequent probes
|
1. **`$TMUX`** — tmux session → `tmux-direct`¹ → `screen-direct`¹, then `tmux-256color`, then `screen-256color`
|
||||||
2. **`$TERM_PROGRAM`** — emulator name hints:
|
2. **`$STY`** — GNU screen session → `screen-direct`¹, then `screen-256color`, then `screen`
|
||||||
|
3. **`$TERM_PROGRAM`** — emulator name hints:
|
||||||
- `iTerm.app` → `xterm-direct`¹, then `xterm-256color`
|
- `iTerm.app` → `xterm-direct`¹, then `xterm-256color`
|
||||||
- `WezTerm` → `xterm-direct`¹, then `wezterm`, then `xterm-256color`
|
- `WezTerm` → `xterm-direct`¹, then `wezterm`, then `xterm-256color`
|
||||||
- `Hyper`, `vscode` → `xterm-direct`¹, then `xterm-256color`
|
- `Hyper`, `vscode` → `xterm-direct`¹, then `xterm-256color`
|
||||||
3. **`$VTE_VERSION`** — GNOME Terminal, Tilix, … → `vte-direct`¹, then `vte-256color`, then `xterm-256color`
|
4. **`$VTE_VERSION`** — GNOME Terminal, Tilix, … → `vte-direct`¹, then `vte-256color`, then `xterm-256color`
|
||||||
4. **`$WT_SESSION`** — Windows Terminal → `xterm-direct`¹, then `xterm-256color`
|
5. **`$WT_SESSION`** — Windows Terminal → `xterm-direct`¹, then `xterm-256color`
|
||||||
5. **`$TMUX`** — tmux session → `tmux-direct`¹ → `screen-direct`¹, then `tmux-256color`, then `screen-256color`
|
6. **Generic probe** — `xterm-256color` → `xterm-color` → `xterm` → `vt100`
|
||||||
6. **`$STY`** — GNU screen session → `screen-direct`¹, then `screen-256color`, then `screen`
|
|
||||||
7. **Generic probe** — `xterm-256color` → `xterm-color` → `xterm` → `vt100`
|
|
||||||
|
|
||||||
¹ Only attempted when the truecolor flag is set (`COLORTERM=truecolor` or `24bit`).
|
¹ Only attempted when the truecolor flag is set (`COLORTERM=truecolor` or `24bit`).
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,17 @@ Versions follow `MAJOR.MINOR.PATCH-REVISION_STAGE_N` (e.g. `3.99.1-4_rc_1`).
|
|||||||
fallback (no `pigz`) works again.
|
fallback (no `pigz`) works again.
|
||||||
- `utaz`: single top-level directory flattening, `--no-dir` target location,
|
- `utaz`: single top-level directory flattening, `--no-dir` target location,
|
||||||
`foo.tar.gz` host directory name and `.tar.lz` extraction.
|
`foo.tar.gz` host directory name and `.tar.lz` extraction.
|
||||||
|
- `conf_save`: no longer replaces a symlinked configuration file by a regular
|
||||||
|
one, keeps the file permissions, rejects multi-line values, and inserts a new
|
||||||
|
key before the trailing comments/banner of its section instead of under the
|
||||||
|
next section's banner; leading blanks before a `[section]` are recognised.
|
||||||
|
- `conf_dump -a`: a commented default is no longer listed when the key is set
|
||||||
|
later in the same section; `NO_COLOR` is honoured; names with newlines or
|
||||||
|
backslashes in arguments are handled.
|
||||||
|
- `term_set`: `COLORTERM=truecolor` no longer short-circuits every other probe
|
||||||
|
(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.
|
||||||
- `compress.sh`: option flags and loop variables no longer leak into the
|
- `compress.sh`: option flags and loop variables no longer leak into the
|
||||||
interactive shell.
|
interactive shell.
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -157,8 +157,9 @@ Set `TERM` in the `[system]` section of `profile.conf`:
|
|||||||
When `TERM` is absent or set to `smart`, `term_set` probes your terminal
|
When `TERM` is absent or set to `smart`, `term_set` probes your terminal
|
||||||
emulator's environment variables (`COLORTERM`, `TERM_PROGRAM`, `VTE_VERSION`,
|
emulator's environment variables (`COLORTERM`, `TERM_PROGRAM`, `VTE_VERSION`,
|
||||||
`WT_SESSION`, `TMUX`, `STY`) and then tests terminfo entries in preference
|
`WT_SESSION`, `TMUX`, `STY`) and then tests terminfo entries in preference
|
||||||
order. If you are experiencing display issues, run `term_set` interactively
|
order (multiplexers first). If you are experiencing display issues, run
|
||||||
and check the result with `echo $TERM`. See *Prompt & theming* below for
|
`TERM=smart; term_set` interactively (`term_set` leaves an already-set `TERM`
|
||||||
|
untouched) and check the result with `echo $TERM`. See *Prompt & theming* below for
|
||||||
symptoms caused by a wrong `TERM` value.
|
symptoms caused by a wrong `TERM` value.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
Executable → Regular
+158
-66
@@ -34,16 +34,44 @@
|
|||||||
# * OF SUCH DAMAGE.
|
# * OF SUCH DAMAGE.
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Print the path of the configuration file the conf_* functions work on:
|
||||||
|
# the user file ($HOME/.profile.conf) when it exists, otherwise the installation
|
||||||
|
# file ($PROFILE_CONF, defaulting to $MYPATH/profile.conf). The returned path
|
||||||
|
# may not exist yet.
|
||||||
|
# Usage: _conf_path
|
||||||
|
_conf_path()
|
||||||
|
{
|
||||||
|
if [[ -f "$HOME/.profile.conf" ]]; then
|
||||||
|
printf '%s\n' "$HOME/.profile.conf"
|
||||||
|
else
|
||||||
|
printf '%s\n' "${PROFILE_CONF:-${MYPATH}/profile.conf}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
export -f _conf_path
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Save or update a key=value pair in a section of the profile configuration file.
|
# Save or update a key=value pair in a section of the profile configuration file.
|
||||||
# The user configuration ($HOME/.profile.conf) is updated when it exists,
|
# The user configuration ($HOME/.profile.conf) is updated when it exists,
|
||||||
# otherwise the installation configuration ($PROFILE_CONF or $MYPATH/profile.conf)
|
# otherwise the installation configuration ($PROFILE_CONF or $MYPATH/profile.conf)
|
||||||
# is used. The section header is created automatically when absent.
|
# is used. The section header is created automatically when absent.
|
||||||
#
|
#
|
||||||
|
# An existing key is replaced in place (duplicates in the same section are
|
||||||
|
# dropped); a new key is added at the end of the section's active lines, before
|
||||||
|
# its trailing blank/comment lines, so it never ends up under the banner of the
|
||||||
|
# next section. The running shell is NOT updated: the value is read again by
|
||||||
|
# parse_conf/load_conf the next time the profile is sourced.
|
||||||
|
#
|
||||||
|
# The value is stored verbatim, but parse_conf trims surrounding whitespace and
|
||||||
|
# quotes, drops backticks and "$(", and expands "$VAR" references when it reads
|
||||||
|
# the file back; values relying on those are therefore not preserved.
|
||||||
|
#
|
||||||
# Usage: conf_save <section> <key> <value>
|
# Usage: conf_save <section> <key> <value>
|
||||||
# section : INI section name without brackets, e.g. "prompt" for [prompt]
|
# section : INI section name without brackets, e.g. "prompt" for [prompt]
|
||||||
# key : variable name to set (alphanumeric and underscore only)
|
# key : variable name to set (alphanumeric and underscore only)
|
||||||
# value : value to assign (may be empty)
|
# value : value to assign (may be empty, must be a single line)
|
||||||
conf_save()
|
conf_save()
|
||||||
{
|
{
|
||||||
if [[ $# -ne 3 ]]; then
|
if [[ $# -ne 3 ]]; then
|
||||||
@@ -61,15 +89,21 @@ conf_save()
|
|||||||
disp E "conf_save: invalid key name '${key}' (alphanumeric and underscore only)."
|
disp E "conf_save: invalid key name '${key}' (alphanumeric and underscore only)."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
# A line break would split the entry and corrupt the file.
|
||||||
local conf_file
|
if [[ "$value" == *$'\n'* || "$value" == *$'\r'* ]]; then
|
||||||
if [[ -f "$HOME/.profile.conf" ]]; then
|
disp E "conf_save: the value must not contain line breaks."
|
||||||
conf_file="$HOME/.profile.conf"
|
return 1
|
||||||
else
|
|
||||||
conf_file="${PROFILE_CONF:-${MYPATH}/profile.conf}"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
local conf_dir="${conf_file%/*}"
|
local conf_file
|
||||||
|
conf_file=$(_conf_path)
|
||||||
|
# Follow a symlink (e.g. dotfiles manager) so the link itself is not
|
||||||
|
# replaced by a regular file when the temporary file is moved over it.
|
||||||
|
[[ -L "$conf_file" ]] && conf_file=$(readlink -f -- "$conf_file")
|
||||||
|
|
||||||
|
# Directory part of the path; a bare file name lives in the current directory.
|
||||||
|
local conf_dir=.
|
||||||
|
[[ "$conf_file" == */* ]] && conf_dir="${conf_file%/*}"
|
||||||
[[ -d "$conf_dir" ]] || mkdir -p "$conf_dir" || {
|
[[ -d "$conf_dir" ]] || mkdir -p "$conf_dir" || {
|
||||||
disp E "conf_save: unable to create configuration directory: ${conf_dir}"
|
disp E "conf_save: unable to create configuration directory: ${conf_dir}"
|
||||||
return 1
|
return 1
|
||||||
@@ -86,26 +120,40 @@ conf_save()
|
|||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Temporary file next to the target so the final mv is an atomic rename.
|
||||||
local tmp_file="${conf_file}.tmp.$$"
|
local tmp_file="${conf_file}.tmp.$$"
|
||||||
# The value is passed via ENVIRON (not awk -v) so backslashes are preserved
|
# The value is passed via ENVIRON (not awk -v) so backslashes are preserved
|
||||||
# verbatim; awk -v would interpret C escape sequences (\t, \n, \e, ...).
|
# verbatim; awk -v would interpret C escape sequences (\t, \n, \e, ...).
|
||||||
|
# awk state machine: in_sec is true while inside the target section, wrote
|
||||||
|
# once the key has been emitted, saw_sec once the section has been seen.
|
||||||
_CONF_SAVE_VAL="$value" \
|
_CONF_SAVE_VAL="$value" \
|
||||||
awk -v sec="$section" -v key="$key" '
|
awk -v sec="$section" -v key="$key" '
|
||||||
|
# Emit the held trailing blank/comment lines
|
||||||
|
function flush_pending( i) {
|
||||||
|
for (i = 1; i <= np; i++) print pend[i]
|
||||||
|
np = 0
|
||||||
|
}
|
||||||
|
|
||||||
BEGIN {
|
BEGIN {
|
||||||
val = ENVIRON["_CONF_SAVE_VAL"]
|
val = ENVIRON["_CONF_SAVE_VAL"]
|
||||||
in_sec = 0
|
in_sec = 0
|
||||||
saw_sec = 0
|
saw_sec = 0
|
||||||
wrote = 0
|
wrote = 0
|
||||||
|
np = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
if ($0 ~ /^\[[^]]+\][[:space:]]*$/) {
|
# Section header (leading blanks allowed, as parse_conf does)
|
||||||
|
if ($0 ~ /^[[:space:]]*\[[^]]+\][[:space:]]*$/) {
|
||||||
|
# Leaving the target section without having found the key:
|
||||||
|
# add it now, before the section trailing comments.
|
||||||
if (in_sec && !wrote) {
|
if (in_sec && !wrote) {
|
||||||
print key "=" val
|
print key "=" val
|
||||||
wrote = 1
|
wrote = 1
|
||||||
}
|
}
|
||||||
|
flush_pending()
|
||||||
|
|
||||||
if ($0 ~ ("^\\[" sec "\\][[:space:]]*$")) {
|
if ($0 ~ ("^[[:space:]]*\\[" sec "\\][[:space:]]*$")) {
|
||||||
in_sec = 1
|
in_sec = 1
|
||||||
saw_sec = 1
|
saw_sec = 1
|
||||||
} else {
|
} else {
|
||||||
@@ -116,6 +164,15 @@ conf_save()
|
|||||||
next
|
next
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Until the key is written, hold blank and comment lines: if the
|
||||||
|
# section ends without the key, it must go before them.
|
||||||
|
if (in_sec && !wrote && $0 ~ /^[[:space:]]*([#;].*)?$/) {
|
||||||
|
pend[++np] = $0
|
||||||
|
next
|
||||||
|
}
|
||||||
|
flush_pending()
|
||||||
|
|
||||||
|
# Existing assignment of the key: replace the first, drop the others
|
||||||
if (in_sec && $0 ~ ("^[[:space:]]*" key "[[:space:]]*=")) {
|
if (in_sec && $0 ~ ("^[[:space:]]*" key "[[:space:]]*=")) {
|
||||||
if (!wrote) {
|
if (!wrote) {
|
||||||
print key "=" val
|
print key "=" val
|
||||||
@@ -130,9 +187,11 @@ conf_save()
|
|||||||
END {
|
END {
|
||||||
if (in_sec && !wrote) {
|
if (in_sec && !wrote) {
|
||||||
print key "=" val
|
print key "=" val
|
||||||
|
wrote = 1
|
||||||
}
|
}
|
||||||
|
flush_pending()
|
||||||
if (!saw_sec) {
|
if (!saw_sec) {
|
||||||
print ""
|
if (NR > 0) print ""
|
||||||
print "[" sec "]"
|
print "[" sec "]"
|
||||||
print key "=" val
|
print key "=" val
|
||||||
}
|
}
|
||||||
@@ -143,7 +202,11 @@ conf_save()
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
mv "$tmp_file" "$conf_file" || {
|
# mktemp-style default permissions must not replace the original ones
|
||||||
|
# (the file may hold private values); best effort, GNU chmod only.
|
||||||
|
chmod --reference="$conf_file" -- "$tmp_file" 2>/dev/null
|
||||||
|
|
||||||
|
mv -- "$tmp_file" "$conf_file" || {
|
||||||
rm -f "$tmp_file"
|
rm -f "$tmp_file"
|
||||||
disp E "conf_save: unable to replace configuration file: ${conf_file}"
|
disp E "conf_save: unable to replace configuration file: ${conf_file}"
|
||||||
return 1
|
return 1
|
||||||
@@ -177,9 +240,8 @@ conf_dump()
|
|||||||
disp E "Invalid options, use \"conf_dump --help\" to display usage."
|
disp E "Invalid options, use \"conf_dump --help\" to display usage."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
local -a _pargs=()
|
# Restore the getopt-normalised (shell-quoted) argument list, "--" ends options
|
||||||
readarray -td '' _pargs < <(printf '%s' "$PARSED" | xargs printf '%s\0')
|
eval set -- "$PARSED"
|
||||||
set -- "${_pargs[@]}"
|
|
||||||
|
|
||||||
while true; do
|
while true; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@@ -219,28 +281,37 @@ conf_dump()
|
|||||||
[[ $# -gt 0 ]] && key_pattern="$1"
|
[[ $# -gt 0 ]] && key_pattern="$1"
|
||||||
|
|
||||||
local conf_file
|
local conf_file
|
||||||
if [[ -f "$HOME/.profile.conf" ]]; then
|
conf_file=$(_conf_path)
|
||||||
conf_file="$HOME/.profile.conf"
|
|
||||||
else
|
|
||||||
conf_file="${PROFILE_CONF:-${MYPATH}/profile.conf}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! -f "$conf_file" ]]; then
|
if [[ ! -f "$conf_file" ]]; then
|
||||||
disp E "conf_dump: configuration file not found: ${conf_file}"
|
disp E "conf_dump: configuration file not found: ${conf_file}"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Colours are passed via ENVIRON to avoid awk -v escape interpretation.
|
# Honour NO_COLOR like disp does: empty colour strings give plain output.
|
||||||
_CONF_DUMP_SEC="${Blue:-}" \
|
local c_sec="${Blue:-}" c_active="${BGreen:-}" c_default="${BIWhite:-}" c_rst="${RESETCOL:-}"
|
||||||
_CONF_DUMP_KEY_ACTIVE="${BGreen:-}" \
|
if [[ -n ${NO_COLOR:-} ]]; then
|
||||||
_CONF_DUMP_KEY_DEFAULT="${BIWhite:-}" \
|
c_sec="" c_active="" c_default="" c_rst=""
|
||||||
_CONF_DUMP_RST="${RESETCOL:-}" \
|
fi
|
||||||
awk -v sec_filter="$section" -v key_filter="$key_pattern" -v show_all="$show_all" '
|
|
||||||
|
# Everything is passed via ENVIRON, not awk -v, to avoid awk interpreting
|
||||||
|
# backslash escapes (colours hold literal \e sequences; a filter could
|
||||||
|
# contain a backslash). The file is given twice: the first pass only
|
||||||
|
# collects the active keys, the second one prints.
|
||||||
|
_CONF_DUMP_SEC="$c_sec" \
|
||||||
|
_CONF_DUMP_KEY_ACTIVE="$c_active" \
|
||||||
|
_CONF_DUMP_KEY_DEFAULT="$c_default" \
|
||||||
|
_CONF_DUMP_RST="$c_rst" \
|
||||||
|
_CONF_DUMP_SECF="$section" \
|
||||||
|
_CONF_DUMP_KEYF="$key_pattern" \
|
||||||
|
awk -v show_all="$show_all" '
|
||||||
BEGIN {
|
BEGIN {
|
||||||
c_sec = ENVIRON["_CONF_DUMP_SEC"]
|
c_sec = ENVIRON["_CONF_DUMP_SEC"]
|
||||||
c_active = ENVIRON["_CONF_DUMP_KEY_ACTIVE"]
|
c_active = ENVIRON["_CONF_DUMP_KEY_ACTIVE"]
|
||||||
c_default = ENVIRON["_CONF_DUMP_KEY_DEFAULT"]
|
c_default = ENVIRON["_CONF_DUMP_KEY_DEFAULT"]
|
||||||
c_rst = ENVIRON["_CONF_DUMP_RST"]
|
c_rst = ENVIRON["_CONF_DUMP_RST"]
|
||||||
|
sec_filter = ENVIRON["_CONF_DUMP_SECF"]
|
||||||
|
key_filter = ENVIRON["_CONF_DUMP_KEYF"]
|
||||||
# Shell colour vars contain literal \e[…m strings; convert to
|
# Shell colour vars contain literal \e[…m strings; convert to
|
||||||
# the actual ESC byte so awk print emits real ANSI sequences.
|
# the actual ESC byte so awk print emits real ANSI sequences.
|
||||||
gsub(/\\e/, "\033", c_sec)
|
gsub(/\\e/, "\033", c_sec)
|
||||||
@@ -251,17 +322,35 @@ conf_dump()
|
|||||||
current_sec = ""
|
current_sec = ""
|
||||||
hdr_printed = 0
|
hdr_printed = 0
|
||||||
found = 0
|
found = 0
|
||||||
# seen[sec:key] — tracks every key already printed to deduplicate
|
# active[sec:key] — keys set (uncommented) anywhere in the file,
|
||||||
# commented entries and avoid re-showing an active key in white.
|
# filled by the first pass.
|
||||||
|
# seen[sec:key] — keys already printed, to show each commented
|
||||||
|
# default at most once.
|
||||||
|
}
|
||||||
|
|
||||||
|
# First pass (FNR == NR): record the active keys, print nothing.
|
||||||
|
# An active key suppresses its commented default wherever the latter
|
||||||
|
# appears in the section (before or after it).
|
||||||
|
FNR == NR {
|
||||||
|
line = $0; sub(/\r$/, "", line)
|
||||||
|
if (line ~ /^[[:space:]]*\[[^]]+\][[:space:]]*$/) {
|
||||||
|
pass1_sec = line
|
||||||
|
sub(/^[[:space:]]*\[/, "", pass1_sec)
|
||||||
|
sub(/\][[:space:]]*$/, "", pass1_sec)
|
||||||
|
} else if (line ~ /^[[:space:]]*[A-Za-z_][A-Za-z0-9_]*[[:space:]]*=/) {
|
||||||
|
k = line; sub(/[[:space:]]*=.*$/, "", k); sub(/^[[:space:]]*/, "", k)
|
||||||
|
active[pass1_sec ":" k] = 1
|
||||||
|
}
|
||||||
|
next
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
sub(/\r$/, "")
|
sub(/\r$/, "")
|
||||||
|
|
||||||
# Section header
|
# Section header (leading blanks allowed, as parse_conf does)
|
||||||
if ($0 ~ /^\[[^]]+\][[:space:]]*$/) {
|
if ($0 ~ /^[[:space:]]*\[[^]]+\][[:space:]]*$/) {
|
||||||
current_sec = $0
|
current_sec = $0
|
||||||
sub(/^\[/, "", current_sec)
|
sub(/^[[:space:]]*\[/, "", current_sec)
|
||||||
sub(/\][[:space:]]*$/, "", current_sec)
|
sub(/\][[:space:]]*$/, "", current_sec)
|
||||||
in_target = (sec_filter == "" || current_sec == sec_filter)
|
in_target = (sec_filter == "" || current_sec == sec_filter)
|
||||||
hdr_printed = 0
|
hdr_printed = 0
|
||||||
@@ -285,7 +374,8 @@ conf_dump()
|
|||||||
next
|
next
|
||||||
}
|
}
|
||||||
|
|
||||||
# Commented-out key=value — shown only with show_all.
|
# Commented-out key=value — shown only with show_all, and only when
|
||||||
|
# the key is not set for real anywhere in the same section.
|
||||||
# The value displayed is the live environment value (what load_conf
|
# The value displayed is the live environment value (what load_conf
|
||||||
# actually exported); when the key is not set in the environment we
|
# actually exported); when the key is not set in the environment we
|
||||||
# fall back to the value written in the comment, i.e. the documented
|
# fall back to the value written in the comment, i.e. the documented
|
||||||
@@ -293,7 +383,7 @@ conf_dump()
|
|||||||
if (show_all && $0 ~ /^[[:space:]]*#[[:space:]]*[A-Za-z_][A-Za-z0-9_]*[[:space:]]*=/) {
|
if (show_all && $0 ~ /^[[:space:]]*#[[:space:]]*[A-Za-z_][A-Za-z0-9_]*[[:space:]]*=/) {
|
||||||
line = $0; sub(/^[[:space:]]*#[[:space:]]*/, "", line)
|
line = $0; sub(/^[[:space:]]*#[[:space:]]*/, "", line)
|
||||||
key = line; sub(/[[:space:]]*=.*$/, "", key); sub(/^[[:space:]]*/, "", key)
|
key = line; sub(/[[:space:]]*=.*$/, "", key); sub(/^[[:space:]]*/, "", key)
|
||||||
if (seen[current_sec ":" key]) next
|
if (active[current_sec ":" key] || seen[current_sec ":" key]) next
|
||||||
seen[current_sec ":" key] = 1
|
seen[current_sec ":" key] = 1
|
||||||
if (key_filter != "" && index(key, key_filter) == 0) next
|
if (key_filter != "" && index(key, key_filter) == 0) next
|
||||||
val = ENVIRON[key]
|
val = ENVIRON[key]
|
||||||
@@ -306,7 +396,7 @@ conf_dump()
|
|||||||
print " " c_default key c_rst "=" val
|
print " " c_default key c_rst "=" val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
' "$conf_file"
|
' "$conf_file" "$conf_file"
|
||||||
}
|
}
|
||||||
export -f conf_dump
|
export -f conf_dump
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
@@ -318,9 +408,11 @@ export -f conf_dump
|
|||||||
#
|
#
|
||||||
# If TERM is already set to a specific value (not empty, not the sentinel
|
# If TERM is already set to a specific value (not empty, not the sentinel
|
||||||
# "smart"), it is honoured as-is — standard Unix behaviour.
|
# "smart"), it is honoured as-is — standard Unix behaviour.
|
||||||
# If TERM is empty or set to "smart", terminal emulator hints are checked first
|
# If TERM is empty or set to "smart", multiplexer hints are checked first
|
||||||
# (COLORTERM, TERM_PROGRAM, VTE_VERSION, WT_SESSION, TMUX, STY), then terminfo
|
# (TMUX, STY), then terminal emulator hints (TERM_PROGRAM, VTE_VERSION,
|
||||||
# is probed in preference order: xterm-256color → xterm-color → xterm → vt100.
|
# WT_SESSION), then terminfo is probed in preference order:
|
||||||
|
# xterm-256color → xterm-color → xterm → vt100. COLORTERM=truecolor|24bit
|
||||||
|
# enables the *-direct (24-bit) entries in those probes.
|
||||||
#
|
#
|
||||||
# Usage: term_set
|
# Usage: term_set
|
||||||
term_set()
|
term_set()
|
||||||
@@ -334,27 +426,46 @@ term_set()
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Return true when terminfo has an entry for the given terminal type.
|
# Return true when terminfo has an entry for the given terminal type.
|
||||||
|
# (Nested functions are global in bash; it is removed with unset -f below.)
|
||||||
local _term_has
|
local _term_has
|
||||||
_term_has()
|
_term_has()
|
||||||
{
|
{
|
||||||
tput -T "$1" longname >/dev/null 2>&1
|
tput -T "$1" longname >/dev/null 2>&1
|
||||||
}
|
}
|
||||||
|
|
||||||
# True-color hint: COLORTERM=truecolor|24bit is set by the emulator.
|
# True-color flag: COLORTERM=truecolor|24bit is set by the emulator. It does
|
||||||
|
# not select an entry by itself; it only enables the *-direct entries in the
|
||||||
|
# probes below, each of which falls back to its *-256color counterpart.
|
||||||
local _truecolor=0
|
local _truecolor=0
|
||||||
[[ "${COLORTERM:-}" == "truecolor" || "${COLORTERM:-}" == "24bit" ]] && _truecolor=1
|
[[ "${COLORTERM:-}" == "truecolor" || "${COLORTERM:-}" == "24bit" ]] && _truecolor=1
|
||||||
|
|
||||||
local _candidate=""
|
local _candidate=""
|
||||||
|
|
||||||
# 1. Explicit truecolor hint set by modern terminal emulators.
|
# Probes run in priority order and stop at the first terminfo entry found
|
||||||
|
# (_candidate non-empty). Multiplexers come first: they provide their own
|
||||||
|
# terminal and inherit the emulator's variables (TERM_PROGRAM, VTE_VERSION,
|
||||||
|
# ...), which would otherwise wrongly select the outer emulator's entry.
|
||||||
|
|
||||||
|
# 1. tmux — prefer *-direct when truecolor, then *-256color, then screen-256color.
|
||||||
|
if [[ -n "${TMUX:-}" ]]; then
|
||||||
if (( _truecolor )); then
|
if (( _truecolor )); then
|
||||||
local _t
|
_term_has "tmux-direct" && _candidate="tmux-direct"
|
||||||
for _t in xterm-direct xterm-256color; do
|
[[ -z "$_candidate" ]] && _term_has "screen-direct" && _candidate="screen-direct"
|
||||||
_term_has "$_t" && { _candidate="$_t"; break; }
|
fi
|
||||||
done
|
[[ -z "$_candidate" ]] && _term_has "tmux-256color" && _candidate="tmux-256color"
|
||||||
|
[[ -z "$_candidate" ]] && _term_has "screen-256color" && _candidate="screen-256color"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 2. Terminal programme name hints.
|
# 2. GNU screen — prefer screen-direct when truecolor, then screen-256color.
|
||||||
|
if [[ -z "$_candidate" && -n "${STY:-}" ]]; then
|
||||||
|
if (( _truecolor )); then
|
||||||
|
_term_has "screen-direct" && _candidate="screen-direct"
|
||||||
|
fi
|
||||||
|
[[ -z "$_candidate" ]] && _term_has "screen-256color" && _candidate="screen-256color"
|
||||||
|
[[ -z "$_candidate" ]] && _term_has "screen" && _candidate="screen"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 3. Terminal programme name hints.
|
||||||
if [[ -z "$_candidate" ]]; then
|
if [[ -z "$_candidate" ]]; then
|
||||||
case "${TERM_PROGRAM:-}" in
|
case "${TERM_PROGRAM:-}" in
|
||||||
iTerm.app)
|
iTerm.app)
|
||||||
@@ -381,7 +492,7 @@ term_set()
|
|||||||
esac
|
esac
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 3. VTE-based terminals (GNOME Terminal, Tilix, Xfce Terminal, …).
|
# 4. VTE-based terminals (GNOME Terminal, Tilix, Xfce Terminal, …).
|
||||||
if [[ -z "$_candidate" && -n "${VTE_VERSION:-}" ]]; then
|
if [[ -z "$_candidate" && -n "${VTE_VERSION:-}" ]]; then
|
||||||
if (( _truecolor )); then
|
if (( _truecolor )); then
|
||||||
_term_has "vte-direct" && _candidate="vte-direct"
|
_term_has "vte-direct" && _candidate="vte-direct"
|
||||||
@@ -390,7 +501,7 @@ term_set()
|
|||||||
[[ -z "$_candidate" ]] && _term_has "xterm-256color" && _candidate="xterm-256color"
|
[[ -z "$_candidate" ]] && _term_has "xterm-256color" && _candidate="xterm-256color"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 4. Windows Terminal.
|
# 5. Windows Terminal.
|
||||||
if [[ -z "$_candidate" && -n "${WT_SESSION:-}" ]]; then
|
if [[ -z "$_candidate" && -n "${WT_SESSION:-}" ]]; then
|
||||||
if (( _truecolor )); then
|
if (( _truecolor )); then
|
||||||
_term_has "xterm-direct" && _candidate="xterm-direct"
|
_term_has "xterm-direct" && _candidate="xterm-direct"
|
||||||
@@ -398,26 +509,7 @@ term_set()
|
|||||||
[[ -z "$_candidate" ]] && _term_has "xterm-256color" && _candidate="xterm-256color"
|
[[ -z "$_candidate" ]] && _term_has "xterm-256color" && _candidate="xterm-256color"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 5. tmux — prefer *-direct when truecolor, then *-256color, then screen-256color.
|
# 6. Generic terminfo probe in preference order.
|
||||||
if [[ -z "$_candidate" && -n "${TMUX:-}" ]]; then
|
|
||||||
if (( _truecolor )); then
|
|
||||||
_term_has "tmux-direct" && _candidate="tmux-direct"
|
|
||||||
[[ -z "$_candidate" ]] && _term_has "screen-direct" && _candidate="screen-direct"
|
|
||||||
fi
|
|
||||||
[[ -z "$_candidate" ]] && _term_has "tmux-256color" && _candidate="tmux-256color"
|
|
||||||
[[ -z "$_candidate" ]] && _term_has "screen-256color" && _candidate="screen-256color"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 6. GNU screen — prefer screen-direct when truecolor, then screen-256color.
|
|
||||||
if [[ -z "$_candidate" && -n "${STY:-}" ]]; then
|
|
||||||
if (( _truecolor )); then
|
|
||||||
_term_has "screen-direct" && _candidate="screen-direct"
|
|
||||||
fi
|
|
||||||
[[ -z "$_candidate" ]] && _term_has "screen-256color" && _candidate="screen-256color"
|
|
||||||
[[ -z "$_candidate" ]] && _term_has "screen" && _candidate="screen"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 7. Generic terminfo probe in preference order.
|
|
||||||
if [[ -z "$_candidate" ]]; then
|
if [[ -z "$_candidate" ]]; then
|
||||||
local _t
|
local _t
|
||||||
for _t in xterm-256color xterm-color xterm vt100; do
|
for _t in xterm-256color xterm-color xterm vt100; do
|
||||||
|
|||||||
Reference in New Issue
Block a user