30 Commits

Author SHA1 Message Date
fatalerrors
1225230a07 4.0 released 2026-04-22 17:56:08 +02:00
fatalerrors
9c43190202 bugfix: removed last french words and frenglish 2026-04-22 17:54:42 +02:00
fatalerrors
9477638a28 bugfix: no error on missing configuration, just use default 2026-04-22 17:16:33 +02:00
fatalerrors
f16ad711fb color adjustments 2026-04-21 16:51:57 +02:00
fatalerrors
15ef317029 themes adjustments 2026-04-21 16:07:16 +02:00
fatalerrors
a6e4d7a256 get ready for release 2026-04-21 15:24:04 +02:00
fatalerrors
6106ca7684 proper package manager detection 2026-04-21 14:18:37 +02:00
fatalerrors
1088029ae6 fix extra shift 2026-04-21 14:03:57 +02:00
fatalerrors
eb4c89759b fix display in kt 2026-04-21 14:03:22 +02:00
fatalerrors
1dc5d72ac6 fix display of return code in prompt 2026-04-21 14:02:45 +02:00
fatalerrors
d49703c5d5 fix themes right 2026-04-18 00:19:07 +02:00
fatalerrors
066f2e353e fix all spellsheck 2026-04-16 17:53:46 +02:00
fatalerrors
c011f03aee add missing help 2026-04-15 17:47:15 +02:00
fatalerrors
c4b0516c45 fix hardcoded setfr in main file 2026-04-15 17:36:15 +02:00
fatalerrors
4da29872fc refining themes 2026-04-15 17:00:04 +02:00
fatalerrors
d7a0e2c5f5 refining themes 2026-04-15 16:53:33 +02:00
fatalerrors
d60a93814b typo 2026-04-15 16:34:54 +02:00
fatalerrors
d27935eedd bugfix 2026-04-15 16:33:45 +02:00
fatalerrors
e77232b3ca bugfix 2026-04-15 16:29:04 +02:00
fatalerrors
d6dce2d91e bugfix 2026-04-15 16:08:34 +02:00
fatalerrors
4e3cccff64 bugfix 2026-04-15 16:03:10 +02:00
fatalerrors
91f033743d bugfix 2026-04-15 15:54:58 +02:00
fatalerrors
9fcf21c55e bugfix 2026-04-15 15:45:55 +02:00
fatalerrors
5300386941 bugfix 2026-04-15 15:39:45 +02:00
fatalerrors
dfad345be3 reset color definitions before loading a new theme 2026-04-15 15:16:11 +02:00
fatalerrors
1b16878ea8 allow dynamic theme change 2026-04-15 15:03:43 +02:00
fatalerrors
30b8b8241a bugfix 2026-04-15 14:54:49 +02:00
fatalerrors
a4056b9e82 fix line ending 2026-04-15 14:53:55 +02:00
fatalerrors
6696e0d05a fix help 2026-04-15 14:44:33 +02:00
fatalerrors
6d15af029e fix mod 2026-04-15 14:25:12 +02:00
36 changed files with 1964 additions and 1597 deletions

15
.gitignore vendored Normal file
View File

@@ -0,0 +1,15 @@
# User-specific configuration — never commit personal settings.
# Copy doc/profile.conf.example to profile.conf and customise it.
profile.conf
# Compiled / generated artefacts
*.pyc
__pycache__/
# macOS noise
.DS_Store
# Editor swap / backup files
*~
*.swp
*.swo

View File

@@ -1,4 +1,4 @@
Copyright 2013-2022 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
Copyright 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
This is distributed with BSD-3-Clause license with the following terms and
condition:

View File

@@ -68,7 +68,8 @@ A bar-style prompt showing current time, execution time of the last command
| `mcd` | filefct | Create a directory and immediately move into it |
| `meteo` | info | Display weather forecast for the configured or given city |
| `myextip` | net | Get information about your public IP address |
| `pkgs` | packages | Search for a pattern in installed package names (dpkg/rpm, supports `-i`) |
| `get_pkgmgr` | packages | Detect the active package manager of the running distribution (`apt`, `dnf`, `yum`, `zypper`, `pacman`, `apk`, `portage`, `xbps`, `nix`) |
| `pkgs` | packages | Search for a pattern in installed package names (distro-aware via `get_pkgmgr`, supports `-i`) |
| `ppg` | processes | Look for the given pattern in running processes |
| `ppn` | processes | List processes matching an exact command name |
| `ppu` | processes | List processes owned by a specific user |
@@ -204,7 +205,7 @@ change the default without having to pass flags every time.
### 4.3. Locale shortcuts
The `[general]` key `SET_LOCALE` accepts a comma-separated list of
The `[lang]` key `SET_LOCALE` accepts a comma-separated list of
`alias:locale` pairs. Each pair generates a function of that name at startup:
```ini
@@ -214,6 +215,16 @@ SET_LOCALE = fr:fr_FR.UTF-8, us:en_US.UTF-8
This creates `setfr` and `setus`. Use `setlocale <locale>` to switch to any
installed locale directly.
Set `DEFAULT_LANG` to one of the defined aliases to activate that locale
automatically at login:
```ini
DEFAULT_LANG = fr
```
If `DEFAULT_LANG` is set but does not match any alias in `SET_LOCALE`, a
warning is displayed and no locale change is applied.
### 4.4. Prompt theming
The prompt appearance is controlled by two mechanisms that are applied in order

View File

@@ -7,6 +7,32 @@ Versions follow `MAJOR.MINOR.PATCH-REVISION_STAGE_N` (e.g. `3.99.1-4_rc_1`).
---
## [3.99.2-4_rc_2] — 2026-04-21
### Fixed
- **`prompt.sh`** — `\$Last_Command` in PS1 was escaped, preventing the exit
code from ever appearing in the prompt (the local variable no longer exists
when PS1 is rendered by bash). Removed the backslash so the value is embedded
at `set_prompt` build time.
- **`filefct.sh``file_stats()`** — a stray unconditional `shift` after
`esac` doubled-shifted arguments already shifted by each `case` branch;
successive options such as `-H -d` were silently skipped.
- **`packages.sh``pkgs()`** — replaced the unreliable binary-presence test
(`command -v dpkg / rpm`) with the new `get_pkgmgr` function. Also corrected
a typo in the "no package manager" error message (`avialable``available`).
- **`processes.sh``kt()`** — copy-paste error: usage error message read
`"Usage: ppg <string>"` instead of `"Usage: kt <pid>"`.
### Added
- **`packages.sh``get_pkgmgr()`** — new exported helper that detects the
active package manager of the running distribution. Detection first reads
`/etc/os-release` (`ID` then `ID_LIKE`), then falls back to a
fixed-priority binary scan. Supported families: `apt`, `dnf`, `yum`,
`zypper`, `pacman`, `apk`, `portage`, `xbps`, `nix`. Returns 1 when
nothing is identified. Available to all future commands in `packages.sh`.
---
## [3.99.1-4_rc_1] — 2026
### Added

View File

@@ -29,9 +29,48 @@ to target). Stale forks cause avoidable merge conflicts.
---
## 3. Development environment
## 3. Branch policy
| Requirement | Minimum version | Notes |
| Branch | Purpose |
|---|---|
| `master` | Main development branch — new features and enhancements go here |
| `<version>` (e.g. `3.x`) | Maintenance branch for a released version — bugfixes backported from `master` |
**New functionality** must always target `master`.
**Bugfixes** must target the branch where the bug was introduced:
- If the bug exists in a released version, open the fix against that version's
maintenance branch first, then cherry-pick onto `master`.
- If the bug is only in `master` (unreleased), fix it directly on `master`.
- During a release-candidate cycle, bugfixes go on the `x.*` branch and are
merged back into `master` before the final release.
Do **not** add new features to a maintenance branch.
---
## 4. Versioning scheme
Versions follow the format **`MAJOR.MINOR.PATCH`** where the `MINOR` number
conveys the development stage of the next major release:
| Minor range | Stage | Rules |
|---|---|---|
| `x.90.y` | **Alpha** toward `x+1` | Stays on `master`. Development is open: new features are welcome, regressions are acceptable. |
| `x.95.y` | **Beta** toward `x+1` | The `x+1.*` maintenance branch is created at this point. No regression unless absolutely necessary; new features still allowed. |
| `x.99.y` | **RC** toward `x+1` | Bugfixes only. No new features. No regression allowed. Becomes `x+1.0.0` when stable. |
Examples: `3.90.1` is the first alpha toward `4.0`, `3.99.2` is the second
release candidate for `4.0`.
The `PATCH` number increments freely within a stage. A bump in `MINOR`
(e.g. `90``95`) always indicates a stage promotion in development phase.
Any experimental version must have it's dedicated branch.
---
## 5. Development environment
|---|---|---|
| Bash | 4.3 | Namerefs (`local -n`) required |
| shellcheck | any recent | Run before every commit |
@@ -52,7 +91,7 @@ brew install shellcheck
---
## 4. Code style
## 6. Code style
### General rules
- **Bash only** — no external interpreters in core modules. Python or Perl is
@@ -62,8 +101,8 @@ brew install shellcheck
- **`[[ … ]]`** for all conditionals — not `[ … ]`.
- **`(( … ))`** for arithmetic — not `$(( … ))` in conditionals.
- **`local`** for all function-internal variables — avoid polluting the
environment.
- **`printf`** instead of `echo` wherever the format matters.
environment. Prefer upper case for global and lowercase for local.
- **`printf`** instead of `echo` all the time.
- **Never `eval`** — use namerefs (`local -n`), `${!varname}` indirection, or
`declare -g` instead.
- **No hardcoded defaults** — wire every configurable value through
@@ -100,7 +139,7 @@ Add the `load_conf` call near the top after any variable declarations.
---
## 5. Configuration keys
## 7. Configuration keys
When adding a configurable default:
@@ -110,7 +149,7 @@ When adding a configurable default:
---
## 6. Theming
## 8. Theming
New theme files go in `profile.d/themes/` with a `.theme` extension.
They are **parsed, not executed** — do not add shell logic.
@@ -118,7 +157,7 @@ See the existing themes and `README.md §4.4` for the allowed syntax.
---
## 7. Running shellcheck
## 9. Running shellcheck
```bash
shellcheck -x profile.sh profile.d/*.sh
@@ -130,7 +169,7 @@ comment explaining why the suppression is necessary.
---
## 8. Submitting a contribution
## 10. Submitting a contribution
### Via Git (preferred)
1. Contact the maintainer to obtain push access, or fork on the Gitea instance.
@@ -156,7 +195,7 @@ Reference issue numbers if applicable: closes #42.
---
## 9. What will be rejected
## 11. What will be rejected
- Code requiring packages not in a minimal Debian or CentOS install.
- Use of `eval`, `source`-based config loading, or other code-injection vectors.
@@ -166,6 +205,6 @@ Reference issue numbers if applicable: closes #42.
---
## 10. Financial contributions
## 12. Financial contributions
Contact the maintainer by mail if you wish to make a financial contribution.

29
doc/LICENSE Executable file
View File

@@ -0,0 +1,29 @@
Copyright 2021-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
This software is distributed under the BSD-3-Clause license with the
following terms and conditions:
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. 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.
3. Neither the name of the copyright holder nor the names of its 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 HOLDER 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, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -89,6 +89,11 @@ TERM=xterm-256color
# creates setfr, setus, setes.
#SET_LOCALE=fr:fr_FR.UTF-8,us:en_US.UTF-8
# Alias to activate at login. Must match one of the aliases defined in SET_LOCALE above.
# Example: DEFAULT_LANG=fr → calls setfr at startup.
# Leave unset to keep the system default locale.
#DEFAULT_LANG=fr
# ==============================================================================
[net]
# dwl: Force a specific download tool (curl, wget, fetch).

View File

@@ -15,9 +15,6 @@ version-bump.
- [ ] **Bash completion** — add a `profile.d/completion/` directory and write
`_profile_upgrade`, `_taz`, `_utaz`, `_meteo`, etc. completions so that
`<Tab>` works on all public functions. **[medium]**
- [ ] **`shellcheck` clean pass** — run `shellcheck -x profile.sh profile.d/*.sh`
and address every remaining warning (currently a handful of SC2034 and SC2086
items). Integrate as a pre-commit hook. **[easy]**
---
@@ -70,10 +67,6 @@ version-bump.
(configurable via `MYEXTIP_FALLBACK_URL`) when the primary times out.
**[easy]**
### packages
- [ ] **Additional backends** — add support for `pacman` (Arch), `apk` (Alpine),
`xbps-query` (Void), and `brew` (macOS). **[medium]**
### processes
- [ ] **`ku` dry-run flag** — add `-n` / `--dry-run` to print what would be
killed without acting. **[easy]**

View File

@@ -44,36 +44,42 @@
# -n, --no-dir Never create a host directory
utaz()
{
# shellcheck disable=SC2329
_ununzip()
{
unzip -o "$1" -d "$2" >/dev/null 2>&1
}
# shellcheck disable=SC2329
_untar()
{
tar -xf "$1" -C "$2"
}
# shellcheck disable=SC2329
_ungzip()
{
tar -xzf "$1" -C "$2"
}
# shellcheck disable=SC2329
_unbzip2()
{
tar -xjf "$1" -C "$2"
}
# shellcheck disable=SC2329
_unxz()
{
tar -xJf "$1" -C "$2"
}
# shellcheck disable=SC2329
_unlzop()
{
lzop -d "$1" -o "$2/$(basename "${1%.*}")"
}
# shellcheck disable=SC2329
_unlzip()
{
if command -v plzip >/dev/null 2>&1; then
@@ -83,16 +89,19 @@ utaz()
fi
}
# shellcheck disable=SC2329
_ununrar()
{
unrar x -o+ "$1" "$2/" >/dev/null 2>&1
}
# shellcheck disable=SC2329
_ununarj()
{
unarj e "$1" "$2/" >/dev/null 2>&1
}
# shellcheck disable=SC2329
_unlha()
{
# lha typically extracts into the current directory
@@ -100,40 +109,47 @@ utaz()
(cd "$2" && lha -x "../$1") >/dev/null 2>&1
}
# shellcheck disable=SC2329
_ununace()
{
unace x "$1" "$2/" >/dev/null 2>&1
}
# shellcheck disable=SC2329
_un7z()
{
7z x "$1" -o"$2/" >/dev/null 2>&1
}
# shellcheck disable=SC2329
_unzstd()
{
# Zstd decompresses files directly, often requiring tar for archives
tar --zstd -xf "$1" -C "$2"
}
# shellcheck disable=SC2329
_uncpio()
{
# CPIO requires careful directory handling
(cd "$2" && cpio -id < "../$1") >/dev/null 2>&1
}
# shellcheck disable=SC2329
_uncabextract()
{
# Requires 'cabextract' package
cabextract "$1" -d "$2/" >/dev/null 2>&1
}
# shellcheck disable=SC2329
_undeb()
{
# Extracts data content from a Debian package
dpkg-deb -x "$1" "$2/" >/dev/null 2>&1
}
# shellcheck disable=SC2329
_unrpm()
{
# Extracts CPIO-based payload from an RPM package
@@ -141,8 +157,9 @@ utaz()
rpm2cpio "$1" | (cd "$2/" && cpio -idmv) >/dev/null 2>&1
}
local PARSED=$(getopt -o hdcn --long help,delete,create-dir,no-dir -n 'utaz' -- "$@")
local PARSED
PARSED=$(getopt -o hdcn --long help,delete,create-dir,no-dir -n 'utaz' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [ $? -ne 0 ]; then
disp E "Invalid options, use \"utaz --help\" to display usage."
return 1
@@ -314,10 +331,10 @@ utaz()
fi
disp I "Processing archive ${f} with ${extractor}..."
mkdir -p "${dir}"
[[ $? -gt 0 ]] &&
if ! mkdir -p "${dir}"; then
disp E "The filesystem can't create directories, exit!" &&
return 1
fi
${extractor} "${f}" "${dir}"
case $? in
@@ -390,6 +407,7 @@ export -f utaz
# -1, .., -9 Compression level to use [1=fast/biggest, 9=slow/smallest]
taz()
{
# shellcheck disable=SC2329
_doxz()
{
command -v xz >/dev/null 2>&1 || {
@@ -397,17 +415,19 @@ taz()
return 127
}
[[ $4 ]] && local verb='-v'
local verb=()
[[ $4 ]] && verb=('-v')
# Display a warning for this format
disp W "xz format is not suited for long term archiving."
disp I "See https://www.nongnu.org/lzip/xz_inadequate.html for details."
# Compresse to xz (lzma2) - Deprecated
xz $verb --compress --keep -$3 -T $2 $1
# Compress with xz (lzma2) - Deprecated
xz "${verb[@]}" --compress --keep "-$3" -T "$2" "$1"
return $?
}
# shellcheck disable=SC2329
_dolz()
{
local procopt="--threads $2"
@@ -425,13 +445,16 @@ taz()
disp W "Consider installing plzip to obtain multithreading abilities."
}
[[ $4 ]] && local verb="-vv"
local opt=()
[[ $4 ]] && opt=('-vv')
opt+=("$procopt")
# Compresse au format lzip (lzma)
$command $verb $procopt --keep -$3 $1
# Compress with lzip (lzma)
$command "${opt[@]}" --keep "-$3" "$1"
return $?
}
# shellcheck disable=SC2329
_dogz()
{
local procopt="--processes $2"
@@ -449,13 +472,16 @@ taz()
disp W "Consider installing pigz to obtain multithreading abilities."
}
[[ $4 ]] && local verb="--verbose"
local opt=()
[[ $4 ]] && opt=('--verbose')
opt+=("$procopt")
# Compresse au format bz2
$command $verb $procopt --keep -$3 $1
$command "${opt[@]}" --keep "-$3" "$1"
return $?
}
# shellcheck disable=SC2329
_dobz2()
{
local procopt="-p$2"
@@ -473,13 +499,16 @@ taz()
disp W "Consider installing pbzip2 to obtain multithreading abilities."
}
[[ $4 ]] && local verb="-v"
local opt=()
[[ $4 ]] && opt=('-v')
opt+=("$procopt")
# Compresse au format bz2
$command $verb --compress $procopt --keep -$3 $1
# Compress with bz2
$command "${opt[@]}" --compress --keep "-$3" "$1"
return $?
}
# shellcheck disable=SC2329
_dolzo()
{
command -v lzop >/dev/null 2>&1 || {
@@ -487,16 +516,18 @@ taz()
return 127
}
[[ $4 ]] && local verb='-v'
local verb=()
[[ $4 ]] && verb=('-v')
[[ $2 -gt 1 ]] && disp W "lzop doesn't support multithreading, falling back to 1 thread."
# Compresse au format lzo
lzop --keep -$3 $1
lzop "${verb[@]}" --keep "-$3" "$1"
return $?
}
local PARSED
PARSED=$(getopt -o hdf:p:vq123456789 --long help,delete,format:,parallel:,verbose,quiet --name "taz" -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [ $? -ne 0 ]; then
disp E "Invalid options, use \"taz --help\" to display usage."
return 1
@@ -587,8 +618,7 @@ taz()
if [[ -d "$item" ]]; then
disp I "\t Creating $item.tar... "
tar -cf "$item.tar" "$item"
if [[ ! $? -eq 0 ]]; then
if ! tar -cf "$item.tar" "$item"; then
disp E "tar file creation failed, skipping to next item."
continue
fi
@@ -602,8 +632,9 @@ taz()
# Skip compression part if tar is asked
if [[ $compform != "tar" ]]; then
disp I "\t Compressing archive..."
_do$compform "$fname" "$nproc" "$complevel" "$verbose"
[[ ! $? -eq 0 ]] && case $? in
local exec_code=0
"_do$compform" "$fname" "$nproc" "$complevel" "$verbose" || exec_code=$?
[[ ! $exec_code -eq 0 ]] && case $exec_code in
127)
disp E "Compression program unavailable, aborting."
return 127

View File

@@ -70,6 +70,7 @@ settrace()
local PARSED
PARSED=$(getopt -oh --long help,on,off,status,force -- "$@")
# 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

View File

@@ -36,81 +36,85 @@
# ------------------------------------------------------------------------------
# Color definitions
# Standard 16 colors display declaration
export DEFAULTFG='\e[0;39m'
export DEFAULTBG='\e[0;49m'
export DEFAULTCOL="${DEFAULTBG}${DEFAULTFG}"
export RESETCOL=$'\e[0m'
set_colors()
{
# Standard 16 colors display declaration
export DEFAULTFG='\e[0;39m'
export DEFAULTBG='\e[0;49m'
export DEFAULTCOL="${DEFAULTBG}${DEFAULTFG}"
export RESETCOL=$'\e[0m'
# Regular Colors
export Black='\e[0;30m'
export Red='\e[0;31m'
export Green='\e[0;32m'
export Yellow='\e[0;33m'
export Blue='\e[0;34m'
export Purple='\e[0;35m'
export Cyan='\e[0;36m'
export White='\e[0;37m'
# Regular Colors
export Black='\e[0;30m'
export Red='\e[0;31m'
export Green='\e[0;32m'
export Yellow='\e[0;33m'
export Blue='\e[0;34m'
export Purple='\e[0;35m'
export Cyan='\e[0;36m'
export White='\e[0;37m'
# Bold
export BBlack='\e[1;30m'
export BRed='\e[1;31m'
export BGreen='\e[1;32m'
export BYellow='\e[1;33m'
export BBlue='\e[1;34m'
export BPurple='\e[1;35m'
export BCyan='\e[1;36m'
export BWhite='\e[1;37m'
# Bold
export BBlack='\e[1;30m'
export BRed='\e[1;31m'
export BGreen='\e[1;32m'
export BYellow='\e[1;33m'
export BBlue='\e[1;34m'
export BPurple='\e[1;35m'
export BCyan='\e[1;36m'
export BWhite='\e[1;37m'
# Underline
export UBlack='\e[4;30m'
export URed='\e[4;31m'
export UGreen='\e[4;32m'
export UYellow='\e[4;33m'
export UBlue='\e[4;34m'
export UPurple='\e[4;35m'
export UCyan='\e[4;36m'
export UWhite='\e[4;37m'
# Underline
export UBlack='\e[4;30m'
export URed='\e[4;31m'
export UGreen='\e[4;32m'
export UYellow='\e[4;33m'
export UBlue='\e[4;34m'
export UPurple='\e[4;35m'
export UCyan='\e[4;36m'
export UWhite='\e[4;37m'
# Background
export On_Black='\e[40m'
export On_Red='\e[41m'
export On_Green='\e[42m'
export On_Yellow='\e[43m'
export On_Blue='\e[44m'
export On_Purple='\e[45m'
export On_Cyan='\e[46m'
export On_White='\e[47m'
# Background
export On_Black='\e[40m'
export On_Red='\e[41m'
export On_Green='\e[42m'
export On_Yellow='\e[43m'
export On_Blue='\e[44m'
export On_Purple='\e[45m'
export On_Cyan='\e[46m'
export On_White='\e[47m'
# High Intensity
export IBlack='\e[0;90m'
export IRed='\e[0;91m'
export IGreen='\e[0;92m'
export IYellow='\e[0;93m'
export IBlue='\e[0;94m'
export IPurple='\e[0;95m'
export ICyan='\e[0;96m'
export IWhite='\e[0;97m'
# High Intensity
export IBlack='\e[0;90m'
export IRed='\e[0;91m'
export IGreen='\e[0;92m'
export IYellow='\e[0;93m'
export IBlue='\e[0;94m'
export IPurple='\e[0;95m'
export ICyan='\e[0;96m'
export IWhite='\e[0;97m'
# Bold High Intensity
export BIBlack='\e[1;90m'
export BIRed='\e[1;91m'
export BIGreen='\e[1;92m'
export BIYellow='\e[1;93m'
export BIBlue='\e[1;94m'
export BIPurple='\e[1;95m'
export BICyan='\e[1;96m'
export BIWhite='\e[1;97m'
# Bold High Intensity
export BIBlack='\e[1;90m'
export BIRed='\e[1;91m'
export BIGreen='\e[1;92m'
export BIYellow='\e[1;93m'
export BIBlue='\e[1;94m'
export BIPurple='\e[1;95m'
export BICyan='\e[1;96m'
export BIWhite='\e[1;97m'
# High Intensity backgrounds
export On_IBlack='\e[0;100m'
export On_IRed='\e[0;101m'
export On_IGreen='\e[0;102m'
export On_IYellow='\e[0;103m'
export On_IBlue='\e[0;104m'
export On_IPurple='\e[0;105m'
export On_ICyan='\e[0;106m'
export On_IWhite='\e[0;107m'
# High Intensity backgrounds
export On_IBlack='\e[0;100m'
export On_IRed='\e[0;101m'
export On_IGreen='\e[0;102m'
export On_IYellow='\e[0;103m'
export On_IBlue='\e[0;104m'
export On_IPurple='\e[0;105m'
export On_ICyan='\e[0;106m'
export On_IWhite='\e[0;107m'
}
export -f set_colors
# ------------------------------------------------------------------------------
@@ -179,5 +183,6 @@ export -f disp
# Load disp section variables
load_conf disp
set_colors
# EOF

View File

@@ -41,8 +41,8 @@ expandlist()
{
local separator="${EXPANDLIST_DEFAULT_SEPARATOR:- }"
local PARSED
PARSED=$(getopt -o hs:n --long help,separator:,newline -n 'expandlist' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [[ $? -ne 0 ]]; then
disp E "Invalid options, use \"expandlist --help\" to display usage."
return 1
@@ -87,6 +87,7 @@ expandlist()
# True glob expansion when wildcards are present.
if [[ "$item" == *'*'* || "$item" == *'?'* || "$item" == *'['* ]]; then
# shellcheck disable=SC2206 # We actually want the word splitting
expanded=( $item )
else
expanded=( "$item" )
@@ -127,7 +128,7 @@ clean()
# Define short and long options
local PARSED
PARSED=$(getopt -o hrsf --long help,recurs,shell,force -n 'clean' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [[ $? -ne 0 ]]; then
disp E "Invalid options, use \"clean --help\" to display usage."
return 1
@@ -215,10 +216,14 @@ mcd()
disp E "Missing parameter. Use \"mcd --help\" to display usage."
return 1
fi
mkdir -pv "$1" && cd "$1" || {
printf "Failed create and/or change directory.\n"
if ! mkdir -pv "$1"; then
disp E "Failed to create directory \"$1\"."
return 1
}
fi
if ! cd "$1"; then
disp E "Failed to change to directory \"$1\"."
return 1
fi
}
export -f mcd
# ------------------------------------------------------------------------------
@@ -241,6 +246,7 @@ rmspc()
local PARSED
PARSED=$(getopt -o hr:c::vs --long help,recursive,subst-char::,verbose,shell -n 'rmspc' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [[ $? -ne 0 ]]; then
disp E "Invalid options, use \"rmspc --help\" to display usage."
return 1
@@ -359,7 +365,7 @@ file_stats()
# Short: H, d, m, M, c, t, a, x:, X:
# Long: human, details, average, median, count, total, all, ext:, ext-list:, min:, max:, help
PARSED=$(getopt -o HdmMctax:X:h --long human,details,average,median,count,total,all,ext:,ext-list:,min:,max:,help -n 'file_stats' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [[ $? -ne 0 ]]; then
disp E "Invalid options, use \"file_stats --help\" to display usage."
return 1
@@ -441,7 +447,6 @@ file_stats()
return 1
;;
esac
shift
done
[[ -n "$1" ]] && path="$1"
@@ -578,6 +583,7 @@ findbig()
local PARSED
PARSED=$(getopt -o hdl:x --long help,details,limit:,one-fs -n 'findbig' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [[ $? -ne 0 ]]; then
disp E "Invalid options, use \"findbig --help\" to display usage."
return 1
@@ -660,6 +666,7 @@ findzero()
local PARSED
# o: options, long: long equivalents
PARSED=$(getopt -o hdx --long help,details,one-fs,delete -n 'findzero' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [[ $? -ne 0 ]]; then
disp E "Invalid options, use \"findzero --help\" to display usage."
return 1
@@ -733,6 +740,7 @@ finddead()
local PARSED
PARSED=$(getopt -o hdx --long help,details,one-fs,delete -n 'finddead' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [[ $? -ne 0 ]]; then
disp E "Invalid options, use \"finddead --help\" to display usage."
return 1

View File

@@ -48,6 +48,7 @@ busy()
# Short: h, p:, d:
# Long: help, pattern:, delay:
PARSED=$(getopt -o hp:d: --long help,pattern:,delay: -n 'busy' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [[ $? -ne 0 ]]; then
disp E "Invalid options, use \"busy --help\" to display usage."
return 1
@@ -89,7 +90,8 @@ busy()
done
# Convert milliseconds to seconds for 'sleep'
local delay_s=$(awk "BEGIN{
local delay_s
delay_s=$(awk "BEGIN{
printf \"%.3f\", $delay_ms / 1000 }")
# Monitor /dev/urandom

View File

@@ -39,45 +39,48 @@
# Usage: help
help()
{
# shellcheck disable=SC2154 # color code in disp.sh
# shellcheck disable=SC2059 # printf format is a color variable
printf "${BIWhite}Welcome to your profile! Here is a list of available commands:${DEFAULTCOL}\n\n"
printf "busy\t\tMonitor /dev/urandom for a hex pattern — look busy\n"
printf "check_updates\tCheck for new versions of profile\n"
printf "clean\t\tErase backup files\n"
printf "clean\t\tErase backup files in given directories, optionally recursive\n"
printf "disp\t\tDisplay formatted info/warning/error/debug messages\n"
printf "dwl\t\tDownload a URL to a local file\n"
printf "expandlist\tExpand and quote item lists\n"
printf "dwl\t\tDownload a URL using curl, wget, or fetch transparently\n"
printf "expandlist\tExpand glob expressions into a quoted, separated list\n"
printf "file_stats\tDisplay file size statistics for a path\n"
printf "findbig\t\tFind biggest files in the given (or current) directory\n"
printf "finddead\tFind dead symbolic links in the given (or current) directory\n"
printf "findzero\tFind empty files in the given (or current) directory\n"
printf "genpwd\t\tGenerate secure passwords\n"
printf "gpid\t\tGive the list of PIDs for the given process name\n"
printf "isipv4\t\tTell if the given IPv4 is valid\n"
printf "isipv6\t\tTell if the given IPv6 is valid\n"
printf "ku\t\tKill process owned by users in parameter\n"
printf "matrix\t\tDisplay matrix-style digital rain\n"
printf "mcd\t\tCreate a directory and go inside\n"
printf "meteo\t\tDisplay current weather forecast for the configured city\n"
printf "myextip\tDisplay current external/public IP\n"
printf "pkgs\t\tSearch for the given package in installed ones\n"
printf "ppg\t\tDisplay process matching the given parameter\n"
printf "ppn\t\tDisplay process matching the exact process name given in parameter\n"
printf "ppu\t\tDisplay processes owned by the given user\n"
printf "profile_upgrade\tUpgrade profile to the latest version\n"
printf "pwdscore\tCalculate password strength score\n"
printf "rain\t\tLet the rain fall\n"
printf "rmhost\t\tRemove host (IP and/or DNS name) from current known_hosts\n"
printf "rmspc\t\tRemove spaces from file and directory names\n"
printf "setlocale\tSet console language to the current locale\n"
printf " * setc\tSet console language to C\n"
printf " * setfr\tSet console language to French\n"
printf " * setus\tSet console language to US English\n"
printf "settrace\tActivate/deactivate call trace for script debugging\n"
printf "showinfo\tShow welcome banner with basic system information\n"
printf "ssr\t\tDo a root login to the given address\n"
printf "taz\t\tCompress smartly the given files or directory\n"
printf "urlencode\tURL-encode the given text\n"
printf "utaz\t\tUncompress archives in the given (or current) directory\n"
printf "ver\t\tDisplay version of your copy of profile\n\n"
printf "findbig\t\tFind the biggest files in the given or current directory\n"
printf "finddead\tFind dead symbolic links in the given or current directory\n"
printf "findzero\tFind empty files in the given or current directory\n"
printf "genpwd\t\tGenerate one or more random secure passwords with configurable constraints\n"
printf "gpid\t\tGive the list of PIDs matching the given process name(s)\n"
printf "isipv4\t\tTell if the given parameter is a valid IPv4 address\n"
printf "isipv6\t\tTell if the given parameter is a valid IPv6 address\n"
printf "ku\t\tKill all processes owned by the given user name or ID\n"
printf "matrix\t\tConsole screensaver with Matrix-style digital rain (binary, kana, ascii charset)\n"
printf "mcd\t\tCreate a directory and immediately move into it\n"
printf "meteo\t\tDisplay weather forecast for the configured or given city\n"
printf "myextip\t\tGet information about your public IP address\n"
printf "pkgs\t\tSearch for a pattern in installed package names (dpkg/rpm, supports -i)\n"
printf "ppg\t\tLook for the given pattern in running processes\n"
printf "ppn\t\tList processes matching an exact command name\n"
printf "ppu\t\tList processes owned by a specific user\n"
printf "profile_upgrade\tUpgrade profile to the latest version (git pull or archive)\n"
printf "pwdscore\tCalculate the strength score of a given password\n"
printf "rain\t\tConsole screensaver with falling-rain effect (multiple color themes)\n"
printf "rmhost\t\tRemove host (name and IP) from SSH known_hosts; supports --all-users as root\n"
printf "rmspc\t\tReplace spaces in filenames with underscores (or a custom character)\n"
printf "setlocale\tSet console locale to any installed locale\n"
printf " * setc\tSet locale to standard C (POSIX)\n"
printf " * set*\tLocale shortcuts generated from SET_LOCALE in profile.conf\n"
printf "settrace\tActivate or deactivate ERR trap to display backtrace on script errors\n"
printf "set_theme\tSwitch the prompt colour theme; no argument lists available themes\n"
printf "showinfo\tDisplay welcome banner and system information (figlet + neofetch/fastfetch)\n"
printf "ssr\t\tSSH into a server as root, forwarding extra ssh options\n"
printf "taz\t\tCompress files and directories into a chosen archive format\n"
printf "urlencode\tURL-encode a string\n"
printf "utaz\t\tSmartly uncompress archives (zip, tar.gz/bz2/xz/lz, rar, arj, lha, ace, 7z, zst, cpio, cab, deb, rpm)\n"
printf "ver\t\tDisplay the installed profile version\n\n"
printf "\nPlease use <command> --help to obtain usage details.\n"
}

View File

@@ -42,6 +42,7 @@ ver()
local PARSED
PARSED=$(getopt -o h --long help -n 'ver' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [[ $? -ne 0 ]]; then
disp E "Invalid options, use \"ver --help\" to display usage."
return 1
@@ -82,6 +83,7 @@ meteo()
local PARSED
PARSED=$(getopt -o h --long help -n 'meteo' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [[ $? -ne 0 ]]; then
disp E "Invalid options, use \"meteo --help\" to display usage."
return 1
@@ -133,6 +135,7 @@ showinfo()
local PARSED
PARSED=$(getopt -o h --long help -n 'showinfo' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [[ $? -ne 0 ]]; then
disp E "Invalid options, use \"showinfo --help\" to display usage."
return 1

View File

@@ -51,6 +51,7 @@ setlocale()
{
local PARSED
PARSED=$(getopt -o h --long help -n 'setlocale' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [[ $? -ne 0 ]]; then
disp E "Invalid options, use \"setlocale --help\" to display usage."
return 1

View File

@@ -70,9 +70,32 @@ dwl()
# Honour preferred tool from configuration; fall back to auto-detection.
local preferred="${DWL_PREFERRED_TOOL:-}"
_try_curl() { [ -z "$output" ] && curl -sL "$url" || curl -sL -o "$output" "$url"; }
_try_wget() { [ -z "$output" ] && wget -qO- "$url" || wget -q -O "$output" "$url"; }
_try_fetch() { [ -z "$output" ] && fetch -o - "$url" || fetch -o "$output" "$url"; }
_try_curl()
{
if [[ -z "$output" ]]; then
curl -sL "$url"
else
curl -sL -o "$output" "$url"
fi
}
_try_wget()
{
if [[ -z "$output" ]]; then
wget -qO- "$url"
else
wget -q -O "$output" "$url"
fi
}
_try_fetch()
{
if [[ -z "$output" ]]; then
fetch -o - "$url"
else
fetch -o "$output" "$url"
fi
}
if [[ -n "$preferred" ]]; then
command -v "$preferred" >/dev/null 2>&1 || {

View File

@@ -34,15 +34,80 @@
# * OF SUCH DAMAGE.
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# Detect the active package manager of the current distribution.
# Detection is based on /etc/os-release (ID / ID_LIKE), then falls back to
# checking available binaries in a fixed priority order.
# Echoes one of: apt dnf yum zypper pacman apk portage xbps nix
# Returns 1 if no known package manager could be identified.
_get_pkgmgr()
{
local distro_id="" distro_like=""
if [[ -r /etc/os-release ]]; then
# shellcheck disable=SC1091
distro_id=$( . /etc/os-release 2>/dev/null; printf '%s' "${ID:-}" )
# shellcheck disable=SC1091
distro_like=$( . /etc/os-release 2>/dev/null; printf '%s' "${ID_LIKE:-}" )
fi
# Map distro IDs/families to a package manager.
# ID_LIKE is space-separated and may list multiple families.
local id
for id in $distro_id $distro_like; do
case "${id,,}" in
debian|ubuntu|linuxmint|raspbian|pop|kali|elementary|zorin|neon|parrot)
echo "apt"; return 0 ;;
fedora)
echo "dnf"; return 0 ;;
rhel|centos|rocky|almalinux|ol|scientific|amzn)
command -v dnf >/dev/null 2>&1 && { echo "dnf"; return 0; }
echo "yum"; return 0 ;;
opensuse*|sles|sled)
echo "zypper"; return 0 ;;
arch|manjaro|endeavouros|garuda|artix|cachyos)
echo "pacman"; return 0 ;;
alpine)
echo "apk"; return 0 ;;
gentoo)
echo "portage"; return 0 ;;
void)
echo "xbps"; return 0 ;;
nixos)
echo "nix"; return 0 ;;
esac
done
# Fallback: check for binaries in priority order.
local bin
for bin in apt-get dnf yum zypper pacman apk emerge xbps-install nix-env; do
command -v "$bin" >/dev/null 2>&1 && {
case "$bin" in
apt-get) echo "apt" ;;
emerge) echo "portage" ;;
xbps-install) echo "xbps" ;;
nix-env) echo "nix" ;;
*) echo "$bin" ;;
esac
return 0
}
done
return 1
}
export -f _get_pkgmgr
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# Look for a package within installed one
# Usage: dpkgs <string>
# Usage: pkgs <string>
pkgs()
{
local ignore_case=${PKGS_DEFAULT_IGNORE_CASE:-0}
local PARSED
PARSED=$(getopt -o hi --long help,ignore-case -n 'pkgs' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [[ $? -ne 0 ]]; then
disp E "Invalid options, use \"pkgs --help\" to display usage."
return 1
@@ -84,13 +149,28 @@ pkgs()
local grep_opt=""
(( ignore_case )) && grep_opt="-i"
command -v dpkg >/dev/null 2>&1 && local cmd="dpkg -l"
command -v rpm >/dev/null 2>&1 && local cmd="rpm -qa"
if [[ -z $cmd ]]; then
disp E "No usable package manager seems unavialable."
local pkgmgr
pkgmgr=$(_get_pkgmgr) || {
disp E "No usable package manager could be detected on this system."
return 2
fi
$cmd | grep $grep_opt $pkg
}
local -a list_cmd
case "$pkgmgr" in
apt) list_cmd=(dpkg-query -l) ;;
dnf|yum|zypper) list_cmd=(rpm -qa) ;;
pacman) list_cmd=(pacman -Q) ;;
apk) list_cmd=(apk list --installed) ;;
portage) list_cmd=(qlist -I) ;;
xbps) list_cmd=(xbps-query -l) ;;
nix) list_cmd=(nix-env -q) ;;
*)
disp E "Package manager '$pkgmgr' is not supported by pkgs."
return 2
;;
esac
"${list_cmd[@]}" | grep ${grep_opt:+"$grep_opt"} "$pkg"
}
export -f pkgs
# ------------------------------------------------------------------------------

View File

@@ -121,6 +121,7 @@ ppn()
# -e: select all processes
# -o: specify custom output columns (PID and Command name)
# grep -w: ensures exact word matching so 'bash' doesn't match 'dbash'
# shellcheck disable=SC2009 # pgrep do not offer the -w switch
ps -eo pid,comm | grep -w "$1"
}
export -f ppn
@@ -200,8 +201,7 @@ ku()
disp E "Usage: ku <username1 [username2 ...]>"
return 1
fi
local users="$@"
for u in $users; do
for u in "$@"; do
if ! id "$u" >/dev/null 2>&1; then
disp E "User '$u' does not exist."
return 1
@@ -227,7 +227,7 @@ kt()
return 0
fi
if [[ -z "$1" ]]; then
disp E "Usage: ppg <string>"
disp E "Usage: kt <pid>"
return 1
fi
@@ -238,7 +238,8 @@ kt()
return 1
fi
local children_pids=$(pgrep -P "$parent_pid")
local children_pids
children_pids=$(pgrep -P "$parent_pid")
for pid in $children_pids; do
kt "$pid" "$@" || break

View File

@@ -140,9 +140,14 @@ load_theme()
continue
fi
# Strip surrounding quotes
_lth_value="${_lth_value#\"}" ; _lth_value="${_lth_value%\"}"
_lth_value="${_lth_value#\'}" ; _lth_value="${_lth_value%\'}"
# Strip surrounding quotes (handles inline trailing comments like KEY="val" # note)
if [[ "$_lth_value" == '"'* ]]; then
_lth_value="${_lth_value#\"}"
_lth_value="${_lth_value%%\"*}"
elif [[ "$_lth_value" == "'"* ]]; then
_lth_value="${_lth_value#\'}"
_lth_value="${_lth_value%%\'*}"
fi
if [[ "$_lth_value" =~ $_lth_ref_re ]]; then
# Safe colour variable reference — resolve via indirection
@@ -159,6 +164,67 @@ load_theme()
fi
done < "$theme_file"
}
# Not exported, it remains private
# ------------------------------------------------------------------------------
# ------------------------------------------------------------------------------
# Dynamically switch the prompt theme for the current shell session.
# Calls load_theme to apply the new colour values immediately, then updates
# PROMPT_THEME so subshells and the set_prompt fallback chain reflect the
# change. PROMPT_THEME_DIR is honoured when set.
# Usage: set_theme [theme_name_or_path]
# With no argument (or -l / --list), lists available .theme files.
set_theme()
{
local theme_dir="${PROMPT_THEME_DIR:-${MYPATH}/profile.d/themes}"
# -- help mode -----------------------------------------------------------
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
printf "set_theme: Switch the prompt colour theme for the current shell session.\n\n"
printf "Usage: set_theme [options] [theme]\n\n"
printf "Options:\n"
printf " -h, --help Display this help screen\n"
printf " -l, --list List available themes (default when no argument is given)\n\n"
printf "Arguments:\n"
printf " theme Bare theme name (e.g. 'dark') or an explicit path to a .theme file.\n"
printf " Themes are searched in: %s\n" "$theme_dir"
printf " Override with PROMPT_THEME_DIR in profile.conf [prompt].\n\n"
printf "Examples:\n"
printf " set_theme — list available themes\n"
printf " set_theme dark — apply the dark theme\n"
printf " set_theme ~/my.theme — apply a theme by path\n"
return 0
fi
# -- list mode -----------------------------------------------------------
if [[ $# -eq 0 || "$1" == "-l" || "$1" == "--list" ]]; then
printf "Available themes in %s:\n" "$theme_dir"
local f name
for f in "$theme_dir"/*.theme; do
[[ -f "$f" ]] || continue
name="${f##*/}"
name="${name%.theme}"
if [[ "$name" == "${PROMPT_THEME:-}" ]]; then
printf " * %s (active)\n" "$name"
else
printf " %s\n" "$name"
fi
done
return 0
fi
# -- apply mode ----------------------------------------------------------
local theme_name="$1"
# Reset colours to defaults before loading the new theme
set_colors
load_theme "$theme_name" || return 1
export PROMPT_THEME="$theme_name"
disp I "Prompt theme set to $theme_name."
}
export -f set_theme
# ------------------------------------------------------------------------------
@@ -186,7 +252,7 @@ function timer_start
# into a human-readable string with appropriate units (us, ms, s, m, h
function timer_stop
{
local delta_us=$((($(timer_now) - $timer_start) / 1000))
local delta_us=$((($(timer_now) - timer_start) / 1000))
local us=$((delta_us % 1000))
local ms=$(((delta_us / 1000) % 1000))
local s=$(((delta_us / 1000000) % 60))
@@ -225,43 +291,48 @@ set_prompt()
# Resolve theme/config colours with hardcoded fallbacks
local _time_fg="${PROMPT_COLOR_TIME_FG:-$Blue}"
local _time_bg="${PROMPT_COLOR_TIME_BG:-$On_IBlack}"
local _time_bg="${PROMPT_COLOR_TIME_BG:-$On_White}"
local _bar_bg="${PROMPT_COLOR_BAR_BG:-$On_Blue}"
local _ok_fg="${PROMPT_COLOR_OK_FG:-$White}"
local _ok_mark="${PROMPT_COLOR_OK_MARK:-$Green}"
local _ok_fg="${PROMPT_COLOR_OK_FG:-$BWhite}"
local _ok_mark="${PROMPT_COLOR_OK_MARK:-$BGreen}"
local _err_bg="${PROMPT_COLOR_ERR_BG:-$On_Red}"
local _err_fg="${PROMPT_COLOR_ERR_FG:-$White}"
local _err_mark="${PROMPT_COLOR_ERR_MARK:-$BYellow}"
local _root_fg="${PROMPT_COLOR_ROOT_FG:-$Red}"
local _user_fg="${PROMPT_COLOR_USER_FG:-$Green}"
local _user_fg="${PROMPT_COLOR_USER_FG:-$BGreen}"
local _dir_fg="${PROMPT_COLOR_DIR_FG:-$ICyan}"
# Begin with time
PS1="\[\e[s${_time_fg}${_time_bg} [ \t ] ${_bar_bg}"
# Begin with time (cursor-save is non-printing; all ANSI sequences wrapped
# in \[...\] so bash does not count them toward the visible line width).
# Every fg colour is combined with its section bg in the same \[...\] block
# so that even "reset" colours (0;Xm) cannot strip the background.
PS1="\[\e[s\]\[${_time_fg}${_time_bg}\] [ \t ] \[${_bar_bg}\]"
# Add exit status of the last command.
# If it was successful, print a green check mark. Otherwise, print a red X.
if [[ $Last_Command == 0 ]]; then
PS1+="${_ok_fg}${_bar_bg} [ \$Last_Command "
PS1+="${_ok_mark}${Checkmark} "
PS1+="\[${_ok_fg}${_bar_bg}\] [ $Last_Command "
PS1+="\[${_ok_mark}${_bar_bg}\]${Checkmark} "
# Add the elapsed time, then close the status section and return to bar bg.
timer_stop
PS1+="($timer_show)\[${_ok_fg}${_bar_bg}\] ] "
else
PS1+="${_err_fg}${_err_bg} [ \$Last_Command "
PS1+="${_err_mark}${FancyX} "
PS1+="\[${_err_fg}${_err_bg}\] [ $Last_Command "
PS1+="\[${_err_mark}${_err_bg}\]${FancyX} "
timer_stop
PS1+="($timer_show)\[${_err_fg}${_err_bg}\] ] "
fi
# Add the elapsed time
timer_stop
PS1+="($timer_show)${_ok_fg} ] ${_bar_bg} "
# If root, print the host in root colour. Otherwise use user colour.
if [[ $EUID -eq 0 ]]; then
PS1+="${_root_fg}\\u${_user_fg}@\\h"
PS1+="\[${_root_fg}${_bar_bg}\] \\u\[${_user_fg}${_bar_bg}\]@\\h"
else
PS1+="${_user_fg}\\u@\\h"
PS1+="\[${_user_fg}${_bar_bg}\] \\u@\\h"
fi
PS1+="\e[K\e[u$DEFAULTCOL\n"
PS1+="\[\e[K\e[u\]\[$RESETCOL\]\n"
# Print the working directory and prompt marker, then reset colour.
PS1+="${_dir_fg}\\w \\\$$DEFAULTCOL "
PS1+="\[${_dir_fg}\]\\w \\\$\[$RESETCOL\] "
}
# ------------------------------------------------------------------------------

View File

@@ -65,6 +65,7 @@ genpwd()
PARSED=$(getopt -o hsnule:L:o: --long \
help,nosymbols,nonumbers,noup,nolow,extracars:,length:,occurences:,occurrences: \
-n 'genpwd' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [[ $? -ne 0 ]]; then return 1; fi
eval set -- "$PARSED"

View File

@@ -44,6 +44,7 @@ rmhost()
local -a known_hosts_files=()
PARSED=$(getopt -o ha --long help,all-users -n 'rmhost' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [[ $? -ne 0 ]]; then return 1; fi
eval set -- "$PARSED"
@@ -194,6 +195,7 @@ ssr()
ssh_default_opts=(-Y)
fi
# shellcheck disable=SC2029
ssh "${ssh_default_opts[@]}" root@"$srv" "$@"
}
export -f ssr

12
profile.d/themes/abyss.theme Executable file → Normal file
View File

@@ -26,16 +26,16 @@ Blue="\e[0;94m" # electric blue (IBlue — abyss identifier colour)
Green="\e[0;96m" # teal (ICyan — abyss string colour)
Yellow="\e[0;93m" # bright gold (IYellow — abyss constant colour)
PROMPT_COLOR_TIME_FG="$ICyan" # electric teal clock
PROMPT_COLOR_TIME_BG="$On_Black" # deep black background
PROMPT_COLOR_BAR_BG="$On_Blue" # deep blue bar
PROMPT_COLOR_TIME_FG="$ICyan" # electric teal clock
PROMPT_COLOR_TIME_BG="\e[48;2;0;60;70m" # very dark cyan bg for time
PROMPT_COLOR_BAR_BG="\e[48;2;0;30;70m" # deep navy bar (24-bit)
PROMPT_COLOR_OK_FG="$ICyan" # teal on success
PROMPT_COLOR_OK_MARK="$IGreen" # bright teal-green checkmark
PROMPT_COLOR_ERR_BG="$On_Red" # red background on failure
PROMPT_COLOR_ERR_FG="$IWhite" # bright white text
PROMPT_COLOR_ERR_MARK="$IYellow" # golden X
PROMPT_COLOR_ERR_BG="\e[48;2;180;20;20m" # vivid crimson background (24-bit)
PROMPT_COLOR_ERR_FG="\e[1;97m" # bold bright white — maximum contrast
PROMPT_COLOR_ERR_MARK="$IYellow" # golden X
PROMPT_COLOR_ROOT_FG="$IRed" # red for root
PROMPT_COLOR_USER_FG="$IBlue" # electric blue for user

4
profile.d/themes/adwaita.theme Executable file → Normal file
View File

@@ -34,9 +34,9 @@ PROMPT_COLOR_OK_FG="$White" # clean white on success
PROMPT_COLOR_OK_MARK="$Green" # Adwaita green checkmark
PROMPT_COLOR_ERR_BG="$On_Red" # Adwaita red on failure
PROMPT_COLOR_ERR_FG="$White" # white text
PROMPT_COLOR_ERR_FG="$BIWhite" # bold bright white for maximum legibility
PROMPT_COLOR_ERR_MARK="$Yellow" # yellow X (warning intent)
PROMPT_COLOR_ROOT_FG="$Red" # Adwaita red for root
PROMPT_COLOR_USER_FG="$IBlue" # Adwaita blue for user
PROMPT_COLOR_USER_FG="$BBlue" # darker bold blue — readable on blue bar
PROMPT_COLOR_DIR_FG="$IGreen" # Adwaita green for path

4
profile.d/themes/dark.theme Executable file → Normal file
View File

@@ -17,8 +17,8 @@
# ------------------------------------------------------------------------------
PROMPT_COLOR_TIME_FG="$ICyan" # Clock text
PROMPT_COLOR_TIME_BG="$On_Black" # Clock background (black)
PROMPT_COLOR_BAR_BG="$On_IBlack" # Main bar background (dark grey)
PROMPT_COLOR_TIME_BG="$On_IBlack" # Clock background (black)
PROMPT_COLOR_BAR_BG="$On_Black" # Main bar background (dark grey)
PROMPT_COLOR_OK_FG="$IGreen" # Exit-code text on success
PROMPT_COLOR_OK_MARK="$BGreen" # Checkmark colour on success

2
profile.d/themes/default.theme Executable file → Normal file
View File

@@ -17,7 +17,7 @@
# ------------------------------------------------------------------------------
PROMPT_COLOR_TIME_FG="$Blue" # Clock text
PROMPT_COLOR_TIME_BG="$On_IBlack" # Clock background (dark grey)
PROMPT_COLOR_TIME_BG="$On_White" # Clock background (dark grey)
PROMPT_COLOR_BAR_BG="$On_Blue" # Main bar background
PROMPT_COLOR_OK_FG="$White" # Exit-code text on success

2
profile.d/themes/light.theme Executable file → Normal file
View File

@@ -19,7 +19,7 @@
# shift to their dark/regular equivalents for contrast on a light terminal.
# ------------------------------------------------------------------------------
PROMPT_COLOR_TIME_FG="$Blue" # Clock text (ICyan → Blue, darker for light bg)
PROMPT_COLOR_TIME_FG="$BBlack" # Clock text (bold black — forces true black on terminals that render Black as dark grey)
PROMPT_COLOR_TIME_BG="$On_IWhite" # Clock background (On_Black → On_IWhite)
PROMPT_COLOR_BAR_BG="$On_White" # Main bar background (On_IBlack → On_White)

0
profile.d/themes/monochrome.theme Executable file → Normal file
View File

0
profile.d/themes/monokai.theme Executable file → Normal file
View File

6
profile.d/themes/plasma.theme Executable file → Normal file
View File

@@ -26,9 +26,9 @@ Blue="\e[0;94m" # electric blue (IBlue)
Purple="\e[0;95m" # vivid magenta (IPurple — Plasma's signature colour)
Cyan="\e[0;96m" # electric cyan (ICyan)
PROMPT_COLOR_TIME_FG="$IPurple" # vivid purple clock text
PROMPT_COLOR_TIME_BG="$On_IBlack" # dark grey background
PROMPT_COLOR_BAR_BG="$On_Purple" # bright magenta bar
PROMPT_COLOR_TIME_FG="$BIPurple" # vivid purple clock text
PROMPT_COLOR_TIME_BG="\e[48;2;50;50;55m" # deep charcoal (darker than On_IBlack)
PROMPT_COLOR_BAR_BG="\e[48;2;75;0;130m" # deep indigo-purple (24-bit) — darker than On_Purple
PROMPT_COLOR_OK_FG="$ICyan" # electric cyan on success
PROMPT_COLOR_OK_MARK="$IGreen" # green checkmark

0
profile.d/themes/solarized-light.theme Executable file → Normal file
View File

8
profile.d/themes/solarized.theme Executable file → Normal file
View File

@@ -108,14 +108,14 @@ RESETCOL="\e[0m"
PROMPT_COLOR_TIME_FG="\e[38;2;181;137;0m" # Yellow — primary accent
PROMPT_COLOR_TIME_BG="\e[48;2;0;43;54m" # Base03 — darkest background
PROMPT_COLOR_BAR_BG="\e[48;2;7;54;66m" # Base02bar background
PROMPT_COLOR_BAR_BG="\e[48;2;88;110;117m" # Base01slightly brighter bar (was Base02)
PROMPT_COLOR_OK_FG="\e[38;2;131;148;150m" # Base0 — body text on success
PROMPT_COLOR_OK_MARK="\e[38;2;133;153;0m" # Green — checkmark
PROMPT_COLOR_ERR_BG="\e[48;2;220;50;47m" # Red — error background
PROMPT_COLOR_ERR_FG="\e[38;2;253;246;227m" # Base3 — bright fg on red
PROMPT_COLOR_ERR_MARK="\e[38;2;181;137;0m" # Yellow — X mark on red bg
PROMPT_COLOR_ERR_BG="\e[48;2;180;20;15m" # deeper crimson — more contrast than Solarized Red
PROMPT_COLOR_ERR_FG="\e[1;38;2;255;255;255m" # bold pure white — maximum contrast on dark red
PROMPT_COLOR_ERR_MARK="\e[1;38;2;253;246;227m" # Base3 bold — bright warm mark stands out on crimson
PROMPT_COLOR_ROOT_FG="\e[38;2;220;50;47m" # Red — root warning
PROMPT_COLOR_USER_FG="\e[38;2;42;161;152m" # Cyan — normal user

View File

@@ -54,6 +54,7 @@ check_updates()
local vfile="" lastver=""
PARSED=$(getopt -o hq --long help,quiet -n 'check_updates' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [[ $? -ne 0 ]]; then
disp E "Invalid options, use \"check_updates --help\" to display usage."
return 2
@@ -128,6 +129,7 @@ profile_upgrade()
local tmpdir="" archive="" extracted_root=""
PARSED=$(getopt -o hf:t:nFb:g --long help,file:,tmpdir:,dry-run,force,branch:,switch-to-git -n 'profile_upgrade' -- "$@")
# shellcheck disable=SC2181 # getopt return code is checked immediately after
if [[ $? -ne 0 ]]; then
disp E "Invalid options, use \"profile_upgrade --help\" to display usage."
return 2
@@ -215,8 +217,8 @@ profile_upgrade()
}
if (( dry_run )); then
disp I "[dry-run] rm -rf \"$MYPATH\"/.git"
disp I "[dry-run] git clone "$BASE_URL" \"$MYPATH\""
[[ -n "$branch" ]] && disp I "[dry-run] git -C \"$MYPATH\" checkout "$branch""
disp I "[dry-run] git clone $BASE_URL \"$MYPATH\""
[[ -n "$branch" ]] && disp I "[dry-run] git -C \"$MYPATH\" checkout $branch"
return 0
fi

View File

@@ -45,7 +45,7 @@ fi
if ((BASH_VERSINFO[0] < 4)) || [[ ${BASH_VERSINFO[0]} -eq 4 && ${BASH_VERSINFO[1]} -lt 3 ]]; then
echo "[ Error ] This profile requires Bash 4.3 or higher."
echo "Current version: $BASH_VERSION"
return 1 2>/dev/null || exit 1
(return 0 2>/dev/null) && return 1 || exit 1
fi
# ------------------------------------------------------------------------------
@@ -66,17 +66,17 @@ pathremove()
export "$pathvar=$newpath"
}
pathprepend()
{
[[ -z "$1" ]] && return 0
local pathvar="${2:-PATH}"
[[ "$pathvar" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] || {
printf "pathprepend: unsafe variable name '%s'\n" "$pathvar" >&2
return 1
}
pathremove "$1" "$pathvar"
export "$pathvar=$1${!pathvar:+:${!pathvar}}"
}
#pathprepend() # Unused for now, but might be useful in the future
#{
# [[ -z "$1" ]] && return 0
# local pathvar="${2:-PATH}"
# [[ "$pathvar" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] || {
# printf "pathprepend: unsafe variable name '%s'\n" "$pathvar" >&2
# return 1
# }
# pathremove "$1" "$pathvar"
# export "$pathvar=$1${!pathvar:+:${!pathvar}}"
#}
pathappend()
{
@@ -98,7 +98,7 @@ parse_conf()
{
local config_file="$1"
local current_section=""
local line key value
local key value
[[ ! -f "$config_file" ]] && return 1
@@ -140,6 +140,7 @@ parse_conf()
# Use a nameref for safe, eval-free assignment
local -n current_array="CONF_$current_section"
# shellcheck disable=SC2034 # Dynamic var creation
current_array["$key"]="$value"
fi
done < "$config_file"
@@ -168,6 +169,7 @@ load_alias()
# Only alias if the base command is executable
if command -v "$base_cmd" >/dev/null 2>&1; then
# shellcheck disable=SC2139 # Dynamic alias creation
alias "$key"="$cmd"
fi
done
@@ -181,7 +183,8 @@ load_conf()
{
local section_name="CONF_$1"
[[ "$(declare -p "$section_name" 2>/dev/null)" != "declare -A"* ]] && return 1
# Missing section is not an error: modules can rely on built-in defaults.
[[ "$(declare -p "$section_name" 2>/dev/null)" != "declare -A"* ]] && return 0
local -n current_vars="$section_name"
@@ -204,22 +207,24 @@ load_conf()
# Because we're more likely to be sourced, we use BASH_SOURCE to get the path
# of the sourced file instead of $0
if [[ -z "$PROFILE_PATH" ]]; then
export MYPATH=$(dirname "$(realpath -s "${BASH_SOURCE[0]}")")
MYPATH=$(dirname "$(realpath -s "${BASH_SOURCE[0]}")")
else
export MYPATH="$PROFILE_PATH"
MYPATH="$PROFILE_PATH"
fi
export MYPATH
if [[ ! -e "$MYPATH/profile.sh" ]]; then
echo "[ Warning ] Path detection failed, trying to use pwd..."
MYPATH=$(pwd)
if [[ ! -e "$MYPATH/profile.sh" ]]; then
echo "[ Error ] Impossible to determine installation path, pretty much nothing will work."
echo "[ Error ] Unable to determine installation path, pretty much nothing will work."
fi
fi
if [[ ! -s "$MYPATH/version" ]]; then
echo "[ Warning ] Impossible to determine running version of profile, your installation might be broken."
echo "[ Warning ] Unable to determine running profile version; your installation might be broken."
fi
export PROFVERSION=$(cat "$MYPATH"/version)
PROFVERSION=$(cat "$MYPATH"/version)
export PROFVERSION
# Build PATH environment variable
if [[ $EUID -eq 0 ]]; then
@@ -258,11 +263,21 @@ if [[ $INTERACTIVE ]]; then
trap 'timer_start' DEBUG
PROMPT_COMMAND='set_prompt'
# Set default language
setfr
# Set default language from DEFAULT_LANG config key (set in [general]).
# The value must match one of the alias names defined in SET_LOCALE so that
# the corresponding set<alias> function exists after build_locale_shortcuts.
if [[ -n "${DEFAULT_LANG:-}" ]]; then
_lang_fn="set${DEFAULT_LANG}"
if declare -F "$_lang_fn" >/dev/null 2>&1; then
"$_lang_fn"
else
disp W "DEFAULT_LANG '$DEFAULT_LANG' has no matching locale shortcut (check SET_LOCALE in profile.conf)."
fi
unset _lang_fn
fi
showinfo && printf "\n"
check_updates -q
disp I "Profile version $PROFVERSION chargé..."
disp I "Profile version $PROFVERSION loaded..."
fi
# Cleanup

View File

@@ -1 +1 @@
3.99.1-4_rc_1
4.0.0