diff --git a/README.md b/README.md index 336bd6a..6c67699 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ A bar-style prompt showing current time, execution time of the last command | `rmhost` | ssh | Remove host(s) (name and IP) from SSH known_hosts; supports `--all-users` as root | | `rmspc` | filefct | Replace spaces in filenames with underscores (or a custom character, attached: `-c-` / `--subst-char=-`); never overwrites an existing file and does not follow symlinked directories | | `setc` | lang | Set locale to standard C (POSIX) | -| `setlocale` | lang | Set console locale to any installed locale | +| `setlocale` | lang | Set console locale to any installed locale (codeset spelling tolerant, UTF-8 preferred when none is given); clears `LC_ALL` and the other per-category variables so the change always applies | | `set_theme` | prompt | Switch the prompt colour theme for the session (`--list`, `--preview`, `--save`) | | `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 + fastfetch/neofetch) | diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index cf659cf..ea68740 100755 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -90,6 +90,12 @@ Versions follow `MAJOR.MINOR.PATCH-REVISION_STAGE_N` (e.g. `3.99.1-4_rc_1`). helper no longer leak into the shell. `showinfo` no longer needs the `hostname` binary. - `showinfo`: `fastfetch` is now preferred over the unmaintained `neofetch`. +- `lang.sh`: `setlocale` (and the `SET_LOCALE` shortcuts) had no effect after + `setc`, because `LC_ALL` stayed set; `setc` and the shortcuts changed the + locale when called with `--help` (and thus through `help `); + `setlocale fr_FR` could pick a non-UTF-8 charset; shortcut functions are built + without evaluating the raw configuration value, and an alias cannot replace + `setlocale` or `setc`. - `compress.sh`: option flags and loop variables no longer leak into the interactive shell. diff --git a/profile.d/lang.sh b/profile.d/lang.sh index 35e867e..90850dd 100644 --- a/profile.d/lang.sh +++ b/profile.d/lang.sh @@ -58,7 +58,7 @@ locale_check() local req_cs_norm="${req_cs,,}" req_cs_norm="${req_cs_norm//[^a-z0-9]/}" - local cand body cs mod cs_norm + local cand body cs mod cs_norm fallback="" while IFS= read -r cand; do [[ -z $cand ]] && continue body="$cand"; cs=""; mod="" @@ -73,10 +73,23 @@ locale_check() # accept any codeset the system provides. [[ -z $req_cs_norm || $cs_norm == "$req_cs_norm" ]] || continue + # With no codeset requested, UTF-8 is preferred: "locale -a" may list + # an older charset first (fr_FR, fr_FR.iso88591, fr_FR.utf8). Any other + # match is kept as a fallback in case no UTF-8 variant exists. + if [[ -z $req_cs_norm && $cs_norm != "utf8" ]]; then + [[ -z $fallback ]] && fallback="$cand" + continue + fi + printf '%s\n' "$cand" return 0 done < <(locale -a 2>/dev/null) + if [[ -n $fallback ]]; then + printf '%s\n' "$fallback" + return 0 + fi + disp W "Locale '$requested' is not installed on this system." return 1 } @@ -95,9 +108,8 @@ setlocale() disp E "Invalid options, use \"setlocale --help\" to display usage." return 1 fi - local -a _pargs=() - readarray -td '' _pargs < <(printf '%s' "$PARSED" | xargs printf '%s\0') - set -- "${_pargs[@]}" + # Restore the getopt-normalised (shell-quoted) argument list + eval set -- "$PARSED" while true; do case "$1" in -h|--help) @@ -125,6 +137,12 @@ setlocale() # various codeset writings (UTF-8, UTF8, utf8, utf-8, ...). loc=$(locale_check "$loc") || return 1 + # LC_ALL overrides every other variable (setc sets it), and so does any + # per-category variable inherited from the environment: drop them all so + # that the new locale really takes effect. + unset LC_ALL LC_ADDRESS LC_IDENTIFICATION LC_MEASUREMENT LC_NAME \ + LC_PAPER LC_TELEPHONE + export LANG=$loc export LC_MESSAGES=$loc export LC_TIME=$loc @@ -144,6 +162,12 @@ export -f setlocale # Usage: setc setc() { + if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then + printf "setc: Set the console locale to the standard C (POSIX) locale.\n" + printf "Usage: setc\n" + return 0 + fi + # Locale definitions export LC_ALL=C export LANG=C @@ -202,19 +226,34 @@ build_locale_shortcuts() fname="set${alias}" + # The generated function must never replace the real commands + if [[ "$fname" == "setlocale" || "$fname" == "setc" ]]; then + disp W "Ignoring locale alias '$alias' in SET_LOCALE: '$fname' is a profile command." + continue + fi + # Optional collision warning if declare -F "$fname" >/dev/null 2>&1; then disp W "Overriding existing function '$fname'." fi - # Build function dynamically - # shellcheck disable=SC2016 - eval "${fname}() { setlocale \"$loc\"; }" + # Build function dynamically. %q quotes the locale so that nothing in + # the configuration value can be interpreted by eval. The shortcut + # answers --help without changing anything ("help " calls it). + local qloc + printf -v qloc '%q' "$loc" + eval "${fname}() { + if [[ \"\${1:-}\" == -h || \"\${1:-}\" == --help ]]; then + printf '%s: switch the console locale (see setlocale --help).\n' \"\${FUNCNAME[0]}\" + return 0 + fi + setlocale ${qloc} + }" # shellcheck disable=SC2163 export -f "$fname" done - unset cfg item alias loc fname locale_items + unset cfg item alias loc fname locale_items qloc } # ------------------------------------------------------------------------------