Compare commits
126 Commits
v3.2.0
...
241d53ebc4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
241d53ebc4 | ||
|
|
1225230a07 | ||
|
|
9c43190202 | ||
|
|
9477638a28 | ||
|
|
f16ad711fb | ||
|
|
15ef317029 | ||
|
|
a6e4d7a256 | ||
|
|
6106ca7684 | ||
|
|
1088029ae6 | ||
|
|
eb4c89759b | ||
|
|
1dc5d72ac6 | ||
|
|
d49703c5d5 | ||
|
|
066f2e353e | ||
|
|
c011f03aee | ||
|
|
c4b0516c45 | ||
|
|
4da29872fc | ||
|
|
d7a0e2c5f5 | ||
|
|
d60a93814b | ||
|
|
d27935eedd | ||
|
|
e77232b3ca | ||
|
|
d6dce2d91e | ||
|
|
4e3cccff64 | ||
|
|
91f033743d | ||
|
|
9fcf21c55e | ||
|
|
5300386941 | ||
|
|
dfad345be3 | ||
|
|
1b16878ea8 | ||
|
|
30b8b8241a | ||
|
|
a4056b9e82 | ||
|
|
6696e0d05a | ||
|
|
6d15af029e | ||
|
|
bc0a592fa1 | ||
|
|
b3f909e287 | ||
|
|
89e20993da | ||
|
|
9a006883b8 | ||
|
|
85f02f4498 | ||
|
|
bb9bbfda16 | ||
|
|
9ea1cc3f72 | ||
|
|
b79103a0a6 | ||
|
|
e5bafe1721 | ||
|
|
322d03ed4c | ||
|
|
60a159c3ea | ||
|
|
84e6fdd429 | ||
|
|
8fe11776cb | ||
|
|
0737d0c647 | ||
|
|
d72fa1a712 | ||
|
|
08e9e6c799 | ||
|
|
ac66e896dd | ||
|
|
0712be626b | ||
|
|
3f8b81562b | ||
|
|
96d1dc695d | ||
|
|
c039ab6ea0 | ||
|
|
6b85556a53 | ||
|
|
cf9a85e61b | ||
|
|
75e047d70e | ||
|
|
742ec484a7 | ||
|
|
1b7262c0cd | ||
|
|
e387209c10 | ||
|
|
f5d59ec194 | ||
|
|
2ee1c935ac | ||
|
|
e41c1a4c51 | ||
|
|
60dfe19049 | ||
|
|
c32771a4ff | ||
|
|
080511d0bd | ||
|
|
d8bdfefdf1 | ||
|
|
5f5f9c0e71 | ||
|
|
30387a4f08 | ||
|
|
0c51363d86 | ||
|
|
043fbaef0b | ||
|
|
ed5587712e | ||
|
|
58cc76c317 | ||
|
|
e82ee06e1d | ||
|
|
bc8cb4a237 | ||
|
|
ae90a9f4c4 | ||
|
|
7e661ca2de | ||
|
|
9f22ed4304 | ||
|
|
1484b004be | ||
|
|
0a4206b890 | ||
|
|
02a1e25df2 | ||
|
|
7ca0a6fb88 | ||
|
|
25df408e37 | ||
|
|
3eab1f98d5 | ||
|
|
6c895b509a | ||
|
|
2ece711e1a | ||
|
|
39a7e7b40f | ||
|
|
6d5d872b71 | ||
|
|
128cfe8c87 | ||
|
|
e1c2705fdd | ||
|
|
368bc11acf | ||
|
|
a068d57ba5 | ||
|
|
ffee8c2e47 | ||
|
|
9ff5792790 | ||
|
|
6a2d9b0fee | ||
|
|
9b2c764181 | ||
|
|
3d5a5e7718 | ||
|
|
47e89b3b09 | ||
|
|
f0f80e2924 | ||
|
|
b08e457146 | ||
|
|
56e34bc346 | ||
|
|
e9e9993dfc | ||
|
|
ff4c6702b7 | ||
|
|
87dea45295 | ||
|
|
0abf481cf6 | ||
|
|
a75299f7b4 | ||
|
|
eeb87c5bfc | ||
|
|
4879b418db | ||
|
|
f944271488 | ||
|
|
4be2e5ea87 | ||
|
|
9d528a6491 | ||
|
|
bef205ae84 | ||
|
|
9e49e3e4d7 | ||
| 94e7e79c76 | |||
| 55e88bd018 | |||
|
|
18f1bc1543 | ||
|
|
0d7c7e9ab7 | ||
|
|
64fecf16fb | ||
| c2ca5f659c | |||
| 3248327e56 | |||
| fd984c4a16 | |||
| c2a0ef0bd2 | |||
| dc64123fd9 | |||
| affe6e1487 | |||
| 0237f8bf70 | |||
| d4db72260a | |||
| e980198c08 | |||
| 21c12865f5 |
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal 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
|
||||||
30
LICENSE
30
LICENSE
@@ -1,11 +1,29 @@
|
|||||||
Copyright (c) <year> <owner>.
|
Copyright 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
This is distributed with BSD-3-Clause license with the following terms and
|
||||||
|
condition:
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
Redistribution and use in source and binary forms, with or without modification,
|
||||||
|
are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
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.
|
1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|||||||
346
README.md
346
README.md
@@ -1,3 +1,347 @@
|
|||||||
# profile
|
# profile
|
||||||
|
This project aims to create an advanced bash profile. It includes aliases,
|
||||||
|
a customized prompt and several functions for different purposes. It's mostly
|
||||||
|
targeted to system administrators but might satisfy some regular users.
|
||||||
|
|
||||||
bash profile repository
|
## 1. Requirements
|
||||||
|
profile requires **Bash 4.3 or higher** (for associative arrays and namerefs).
|
||||||
|
It will refuse to load on older versions and will also refuse to load if the
|
||||||
|
current shell is not bash.
|
||||||
|
|
||||||
|
## 2. Getting started
|
||||||
|
Download and extract (or use git clone) the profile archive into your home
|
||||||
|
directory. You will have to modify your `~/.bashrc` and/or `~/.profile` file to
|
||||||
|
add at the end (preferably):
|
||||||
|
```bash
|
||||||
|
source <installpath>/profile/profile.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
You may also set the `PROFILE_PATH` environment variable before sourcing if you
|
||||||
|
want to override the automatic path detection:
|
||||||
|
```bash
|
||||||
|
export PROFILE_PATH=/opt/profile
|
||||||
|
source /opt/profile/profile.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
It's not recommended to load that profile in `/etc/profile` as users' `.bashrc`
|
||||||
|
files might interfere with some aliases and functions defined in profile.
|
||||||
|
|
||||||
|
### 2.1. Initial configuration
|
||||||
|
Copy the example configuration file and customise it to your needs:
|
||||||
|
```bash
|
||||||
|
cp <installpath>/profile/doc/profile.conf.example <installpath>/profile/profile.conf
|
||||||
|
```
|
||||||
|
`profile.conf` is git-ignored so your personal settings will never be
|
||||||
|
accidentally committed. All keys are optional — sensible defaults apply when
|
||||||
|
unset. See [section 4](#4-configuration) for the full reference.
|
||||||
|
|
||||||
|
## 3. What's the purpose?
|
||||||
|
profile gives access to numerous functions, aliases and to an advanced prompt.
|
||||||
|
All functions are organized into modules under the `profile.d/` directory and
|
||||||
|
are loaded automatically at startup.
|
||||||
|
|
||||||
|
### 3.1. Prompt
|
||||||
|
A bar-style prompt showing current time, execution time of the last command
|
||||||
|
(with sub-millisecond precision), and the exit code of the last command.
|
||||||
|
|
||||||
|
### 3.2. Functions reference
|
||||||
|
|
||||||
|
| Function | Module | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `busy` | fun | Monitor /dev/urandom for a hex pattern — look busy |
|
||||||
|
| `check_updates` | updates | Check whether a newer profile version is available online |
|
||||||
|
| `clean` | filefct | Erase backup files in given directories, optionally recursive |
|
||||||
|
| `disp` | disp | Display formatted info / warning / error / debug messages |
|
||||||
|
| `dwl` | net | Download a URL using curl, wget, or fetch transparently |
|
||||||
|
| `expandlist` | filefct | Expand glob expressions into a quoted, separated list |
|
||||||
|
| `file_stats` | filefct | Display file size statistics for a path |
|
||||||
|
| `findbig` | filefct | Find the biggest files in the given or current directory |
|
||||||
|
| `finddead` | filefct | Find dead symbolic links in the given or current directory |
|
||||||
|
| `findzero` | filefct | Find empty files in the given or current directory |
|
||||||
|
| `genpwd` | pwd | Generate one or more random secure passwords with configurable constraints |
|
||||||
|
| `gpid` | processes | Give the list of PIDs matching the given process name(s) |
|
||||||
|
| `help` | help | Display the list of available functions and basic usage |
|
||||||
|
| `isipv4` | net | Tell if the given parameter is a valid IPv4 address |
|
||||||
|
| `isipv6` | net | Tell if the given parameter is a valid IPv6 address |
|
||||||
|
| `ku` | processes | Kill all processes owned by the given user name or ID |
|
||||||
|
| `matrix` | rain | Console screensaver with Matrix-style digital rain (binary, kana, ascii charset) |
|
||||||
|
| `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 |
|
||||||
|
| `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 |
|
||||||
|
| `profile_upgrade` | updates | Upgrade profile to the latest version (git pull or archive) |
|
||||||
|
| `pwdscore` | pwd | Calculate the strength score of a given password |
|
||||||
|
| `rain` | rain | Console screensaver with falling-rain effect (multiple color themes) |
|
||||||
|
| `rmhost` | ssh | Remove host (name and IP) from SSH known_hosts; supports `--all-users` as root |
|
||||||
|
| `rmspc` | filefct | Replace spaces in filenames with underscores (or a custom character) |
|
||||||
|
| `setc` | lang | Set locale to standard C (POSIX) |
|
||||||
|
| `setlocale` | lang | Set console locale to any installed locale |
|
||||||
|
| `settrace` | debug | Activate or deactivate ERR trap to display backtrace on script errors |
|
||||||
|
| `showinfo` | info | Display welcome banner and system information (figlet + neofetch/fastfetch) |
|
||||||
|
| `ssr` | ssh | SSH into a server as root, forwarding extra ssh options |
|
||||||
|
| `taz` | compress | Compress files and directories into a chosen archive format |
|
||||||
|
| `urlencode` | net | URL-encode a string |
|
||||||
|
| `utaz` | compress | Smartly uncompress archives (zip, tar.gz/bz2/xz/lz, rar, arj, lha, ace, 7z, zst, cpio, cab, deb, rpm) |
|
||||||
|
| `ver` | info | Display the installed profile version |
|
||||||
|
|
||||||
|
Locale shortcut functions (`setfr`, `setus`, etc.) are dynamically generated at
|
||||||
|
startup from the `SET_LOCALE` configuration key (see section 4).
|
||||||
|
|
||||||
|
## 4. Configuration
|
||||||
|
profile uses an INI-style configuration file (`profile.conf`) located in the
|
||||||
|
same directory as `profile.sh`. Sections are declared with `[section_name]` and
|
||||||
|
keys follow `key = value` syntax. Each module calls `load_conf "<section>"` at
|
||||||
|
load time, which exports every key in that section as an environment variable.
|
||||||
|
Unknown keys are silently ignored; all keys are optional — sensible defaults
|
||||||
|
apply when unset.
|
||||||
|
|
||||||
|
`profile.conf` is listed in `.gitignore` so personal values (API keys, cities,
|
||||||
|
compiler flags, …) are never accidentally staged. Start from the annotated
|
||||||
|
template at `doc/profile.conf.example` (see [section 2.1](#21-initial-configuration)).
|
||||||
|
|
||||||
|
### 4.1. Core sections
|
||||||
|
|
||||||
|
| Section | Purpose |
|
||||||
|
|---|---|
|
||||||
|
| `[system]` | Bash history size, pager, and other shell behaviours |
|
||||||
|
| `[general]` | General-purpose variables (e.g. compilation flags, `MAKEFLAGS`) |
|
||||||
|
| `[aliases]` | User command aliases, loaded for interactive shells only |
|
||||||
|
|
||||||
|
### 4.2. Module defaults
|
||||||
|
|
||||||
|
Each module exposes its hardcoded defaults as configuration keys. Set a key to
|
||||||
|
change the default without having to pass flags every time.
|
||||||
|
|
||||||
|
**`[compress]`** — `taz` / `utaz`
|
||||||
|
|
||||||
|
| Key | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `TAZ_DEFAULT_FORMAT` | `tar.gz` | Archive format for `taz` (`tar.gz`, `tar.bz2`, `tar.xz`, `zip`, …) |
|
||||||
|
| `TAZ_DEFAULT_THREADS` | `0` | Compression threads (0 = auto-detect) |
|
||||||
|
| `TAZ_DEFAULT_LEVEL` | `6` | Compression level (1–9) |
|
||||||
|
| `UTAZ_DEFAULT_DELETE` | `0` | Set to `1` to delete the source archive after extraction |
|
||||||
|
| `UTAZ_DEFAULT_DIR_MODE` | `0` | Set to `1` to always extract into a subdirectory |
|
||||||
|
|
||||||
|
**`[filefct]`** — file utilities
|
||||||
|
|
||||||
|
| Key | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `EXPANDLIST_DEFAULT_SEPARATOR` | ` ` (space) | Separator used by `expandlist` |
|
||||||
|
| `CLEAN_DEFAULT_RECURSIVE` | `0` | Set to `1` to make `clean` recurse into subdirectories |
|
||||||
|
| `RMSPC_DEFAULT_CHAR` | `_` | Replacement character used by `rmspc` |
|
||||||
|
| `FINDBIG_DEFAULT_LIMIT` | `10` | Number of results returned by `findbig` |
|
||||||
|
|
||||||
|
**`[rain]`** — screensavers
|
||||||
|
|
||||||
|
| Key | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `RAIN_DEFAULT_SPEED` | `0.1` | Falling speed for `rain` |
|
||||||
|
| `RAIN_DEFAULT_COLOR` | `Green` | Colour for `rain` |
|
||||||
|
| `MATRIX_DEFAULT_SPEED` | `0.05` | Falling speed for `matrix` |
|
||||||
|
| `MATRIX_DEFAULT_COLOR` | `Green` | Colour for `matrix` |
|
||||||
|
| `MATRIX_DEFAULT_CHARSET` | `binary` | Character set for `matrix` (`binary`, `kana`, `ascii`) |
|
||||||
|
|
||||||
|
**`[ssh]`**
|
||||||
|
|
||||||
|
| Key | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `SSH_DEFAULT_OPT` | _(empty)_ | Extra options passed to `ssr` (e.g. `-Y` for X forwarding) |
|
||||||
|
|
||||||
|
**`[pwd]`** — password tools
|
||||||
|
|
||||||
|
| Key | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `GENPWD_DEFAULT_LENGTH` | `16` | Generated password length |
|
||||||
|
| `GENPWD_DEFAULT_OCCURS` | `1` | Number of character class occurrences |
|
||||||
|
| `GENPWD_DEFAULT_COUNT` | `1` | Number of passwords to generate |
|
||||||
|
| `GENPWD_DEFAULT_SYMBOLS` | `1` | Include symbols (0/1) |
|
||||||
|
| `GENPWD_DEFAULT_UPPERCASE` | `1` | Include uppercase letters (0/1) |
|
||||||
|
| `GENPWD_DEFAULT_LOWERCASE` | `1` | Include lowercase letters (0/1) |
|
||||||
|
| `GENPWD_DEFAULT_NUMBERS` | `1` | Include digits (0/1) |
|
||||||
|
| `PWDSCORE_DEFAULT_VERBOSE` | `0` | Set to `1` for detailed scoring output from `pwdscore` |
|
||||||
|
|
||||||
|
**`[fun]`**
|
||||||
|
|
||||||
|
| Key | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `BUSY_DEFAULT_PATTERN` | `[0-9a-f]` | Hex pattern matched by `busy` |
|
||||||
|
| `BUSY_DEFAULT_DELAY` | `0.1` | Polling delay (seconds) for `busy` |
|
||||||
|
|
||||||
|
**`[info]`**
|
||||||
|
|
||||||
|
| Key | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `METEO_DEFAULT_CITY` | _(empty)_ | Default city for `meteo` when no argument is given |
|
||||||
|
|
||||||
|
**`[net]`**
|
||||||
|
|
||||||
|
| Key | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `DWL_PREFERRED_TOOL` | _(empty)_ | Force `dwl` to use `curl`, `wget`, or `fetch` (auto-detected when unset) |
|
||||||
|
| `MYEXTIP_DEFAULT_URL` | `https://ip-api.com/json` | API endpoint used by `myextip` |
|
||||||
|
|
||||||
|
**`[packages]`**
|
||||||
|
|
||||||
|
| Key | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `PKGS_DEFAULT_IGNORE_CASE` | `0` | Set to `1` to make `pkgs` case-insensitive by default |
|
||||||
|
|
||||||
|
**`[processes]`**
|
||||||
|
|
||||||
|
| Key | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `PPU_DEFAULT_FORMAT` | `pid,user,comm,args` | `ps` output format used by `ppu` |
|
||||||
|
| `KU_DEFAULT_SIGNAL` | `TERM` | Default signal sent by `ku` |
|
||||||
|
|
||||||
|
**`[updates]`**
|
||||||
|
|
||||||
|
| Key | Default | Description |
|
||||||
|
|---|---|---|
|
||||||
|
| `UPDT_DEFAULT_BRANCH` | `master` | Git branch used for update checks and `profile_upgrade`. Changing this value causes `profile_upgrade` to automatically switch the local checkout to the new branch on the next run and display a warning. |
|
||||||
|
|
||||||
|
### 4.3. Locale shortcuts
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
(later values win):
|
||||||
|
|
||||||
|
1. **Theme file** — sets a base colour palette.
|
||||||
|
2. **`[prompt]` section** in `profile.conf` — per-key overrides on top of the theme.
|
||||||
|
|
||||||
|
**Selecting a theme:**
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[prompt]
|
||||||
|
PROMPT_THEME = dark # bare name → profile.d/themes/dark.theme
|
||||||
|
PROMPT_THEME_DIR = ~/.mythemes # optional: custom search directory
|
||||||
|
```
|
||||||
|
|
||||||
|
Built-in themes: `default`, `dark`, `light`, `solarized`, `solarized-light`,
|
||||||
|
`monokai`, `monochrome`, `abyss`, `plasma`, `adwaita`.
|
||||||
|
|
||||||
|
**Overriding individual prompt colour slots:**
|
||||||
|
|
||||||
|
```ini
|
||||||
|
[prompt]
|
||||||
|
PROMPT_COLOR_USER_FG = $ICyan
|
||||||
|
PROMPT_COLOR_DIR_FG = $IYellow
|
||||||
|
```
|
||||||
|
|
||||||
|
The eleven available `PROMPT_COLOR_*` keys are:
|
||||||
|
|
||||||
|
| Key | Role |
|
||||||
|
|---|---|
|
||||||
|
| `PROMPT_COLOR_TIME_FG` / `TIME_BG` | Clock foreground / background |
|
||||||
|
| `PROMPT_COLOR_BAR_BG` | Main bar background |
|
||||||
|
| `PROMPT_COLOR_OK_FG` / `OK_MARK` | Exit-code text / checkmark on success |
|
||||||
|
| `PROMPT_COLOR_ERR_BG` / `ERR_FG` / `ERR_MARK` | Error bar background / text / X mark |
|
||||||
|
| `PROMPT_COLOR_ROOT_FG` | Username colour when running as root |
|
||||||
|
| `PROMPT_COLOR_USER_FG` | Username@host colour for normal users |
|
||||||
|
| `PROMPT_COLOR_DIR_FG` | Working directory colour |
|
||||||
|
|
||||||
|
**Writing a custom theme file:**
|
||||||
|
|
||||||
|
Theme files live in `profile.d/themes/` and use the `.theme` extension. They
|
||||||
|
are **parsed, not executed** — no shell code runs. Only two value forms are
|
||||||
|
accepted:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
# Colour variable reference (resolved from profile.d/disp.sh)
|
||||||
|
PROMPT_COLOR_DIR_FG = "$ICyan"
|
||||||
|
|
||||||
|
# Raw ANSI escape sequence (single block, 16-colour or 24-bit true-colour)
|
||||||
|
PROMPT_COLOR_BAR_BG = "\e[48;2;7;54;66m"
|
||||||
|
```
|
||||||
|
|
||||||
|
Any unknown key, unsafe value, or shell construct is discarded with a warning.
|
||||||
|
Theme files may also override the standard colour variables (`Black`, `Blue`,
|
||||||
|
`On_IBlack`, etc.) to remap the entire terminal palette used by `ls`, `grep`,
|
||||||
|
and other colour-aware tools.
|
||||||
|
|
||||||
|
True-colour themes (`solarized`, `solarized-light`) require a terminal with
|
||||||
|
24-bit colour support (Konsole, iTerm2, kitty, Alacritty, Windows Terminal).
|
||||||
|
Verify support with:
|
||||||
|
```bash
|
||||||
|
printf '\e[38;2;38;139;210mTrue colour test\e[0m\n'
|
||||||
|
```
|
||||||
|
|
||||||
|
## 5. Contact and more information
|
||||||
|
### 5.1. New users
|
||||||
|
If you use (or plan to use) `profile`, I'll be happy if you simply mail me to
|
||||||
|
let me know, especially if you don't plan to contribute. If you plan to
|
||||||
|
contribute, I'll be twice happier for sure!
|
||||||
|
|
||||||
|
### 5.2. Bugs
|
||||||
|
**profile** bug tracker is hosted on its Gitea instance. Check the
|
||||||
|
https://git.geoffray-levasseur.org/fatalerrors/profile page. If you find a bug,
|
||||||
|
you can also submit a bug report to the maintainer mail address mentioned at
|
||||||
|
the end of that document. A bug report may contain the command line parameters
|
||||||
|
where the bug happens, OS details, the module that triggered it, if any, and the
|
||||||
|
log file containing the error. Cygwin users: please note that the bash
|
||||||
|
implementation in Cygwin regularly triggers bugs on advanced code that works
|
||||||
|
fine on Linux or BSD. Please do not send syntax error bug reports if you
|
||||||
|
have not tested the same code under a real Unix environment.
|
||||||
|
|
||||||
|
Check the [FAQ](./doc/FAQ.md) and the [to-do list](./doc/todo.md) before
|
||||||
|
sending any feature request or bug report, as it might already be documented.
|
||||||
|
|
||||||
|
### 5.3. How to contribute?
|
||||||
|
You are free to improve and contribute as you wish. If you have no idea what to
|
||||||
|
do or want some direction, you can check the [to-do list](./doc/todo.md),
|
||||||
|
containing desired future improvements. Make sure you always have the latest
|
||||||
|
development version before starting your work.
|
||||||
|
|
||||||
|
Read [CONTRIBUTING.md](./doc/CONTRIBUTING.md) for code style conventions,
|
||||||
|
branch workflow, and how to submit a patch or pull request.
|
||||||
|
|
||||||
|
It's heavily recommended to use git to obtain the latest copy of the profile
|
||||||
|
tree. Make sure your git configuration is correct in order to contribute.
|
||||||
|
Please contact me to obtain push authorizations, or, if you want to submit a
|
||||||
|
patch, you can send it by mail to the maintainer of profile.
|
||||||
|
|
||||||
|
Code written in Python or Perl might be accepted as long as it is not
|
||||||
|
mobilizing a lot of dependencies (forget big frameworks). Anything that
|
||||||
|
requires installing packages not provided in a minimal Debian or CentOS
|
||||||
|
installation will probably be rejected.
|
||||||
|
|
||||||
|
If you want to make a financial contribution, please contact me by mail.
|
||||||
|
|
||||||
|
### 5.4. License, website, and maintainer
|
||||||
|
Everything except configuration files is licensed under the BSD-3 license.
|
||||||
|
Please check the license file alongside this one.
|
||||||
|
|
||||||
|
Please check [https://www.geoffray-levasseur.org/profile](https://www.geoffray-levasseur.org/profile).
|
||||||
|
|
||||||
|
You can mail the author at fatalerrors \<at\> geoffray-levasseur \<dot\> org.
|
||||||
|
|
||||||
|
-----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Documentation (c) 2021-2026 Geoffray Levasseur.
|
||||||
|
|
||||||
|
This file is distributed under the 3-clause BSD license. The complete license
|
||||||
|
agreement can be obtained at: https://opensource.org/licenses/BSD-3-Clause
|
||||||
|
|||||||
97
doc/CHANGELOG.md
Executable file
97
doc/CHANGELOG.md
Executable file
@@ -0,0 +1,97 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
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
|
||||||
|
- **Theming system** — `load_theme` in `profile.d/prompt.sh` loads `.theme`
|
||||||
|
files from `profile.d/themes/` (or a custom directory set via
|
||||||
|
`PROMPT_THEME_DIR`). Theme files are **parsed, not executed** — no shell code
|
||||||
|
ever runs from a theme.
|
||||||
|
- **Built-in themes** — `default`, `dark`, `light`, `solarized` (24-bit
|
||||||
|
true-colour), `solarized-light` (24-bit), `monokai`, `monochrome`, `abyss`,
|
||||||
|
`plasma`, `adwaita`.
|
||||||
|
- **`[prompt]` section** — `PROMPT_THEME` and `PROMPT_THEME_DIR` keys to select
|
||||||
|
a theme; eleven `PROMPT_COLOR_*` keys for per-key overrides in `profile.conf`.
|
||||||
|
- **Standard colour variables as theme keys** — theme files may also override
|
||||||
|
the `disp.sh` colour variables (`Black`, `Blue`, `On_IBlack`, …) to remap the
|
||||||
|
terminal palette used by `ls`, `grep`, and colour-aware tools.
|
||||||
|
- **Module configuration defaults** — all modules now expose their hardcoded
|
||||||
|
defaults as `profile.conf` keys: `TAZ_*`, `UTAZ_*`, `EXPANDLIST_*`,
|
||||||
|
`CLEAN_*`, `RMSPC_*`, `FINDBIG_*`, `RAIN_*`, `MATRIX_*`, `SSH_DEFAULT_OPT`,
|
||||||
|
`GENPWD_*`, `PWDSCORE_*`, `BUSY_*`, `METEO_DEFAULT_CITY`, `DWL_*`,
|
||||||
|
`MYEXTIP_*`, `PKGS_*`, `PPU_*`, `KU_*`, `UPDT_DEFAULT_BRANCH`.
|
||||||
|
- **`UPDT_DEFAULT_BRANCH`** — configures the git branch used by `check_updates`
|
||||||
|
and `profile_upgrade`. Changing the key causes `profile_upgrade` to
|
||||||
|
automatically switch the local checkout to the new branch and display a
|
||||||
|
warning.
|
||||||
|
- **`doc/` directory** — `CONTRIBUTING.md`, `FAQ.md`, `profile.conf.example`
|
||||||
|
(annotated template), `todo.md`.
|
||||||
|
- **`.gitignore`** — `profile.conf` is now git-ignored so personal settings are
|
||||||
|
never accidentally staged.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- README §2 now explains how to create `profile.conf` from
|
||||||
|
`doc/profile.conf.example` (new section 2.1 "Initial configuration").
|
||||||
|
- README §4 updated with full module-defaults tables, theming reference, and a
|
||||||
|
note about `profile.conf` being git-ignored.
|
||||||
|
- Theme values no longer carry the `export` keyword (they are not shell
|
||||||
|
variables, only data).
|
||||||
|
|
||||||
|
### Security
|
||||||
|
- `load_theme` uses a strict allowlist (no `eval`, no sourcing). Only
|
||||||
|
`PROMPT_COLOR_*` keys and known `disp.sh` colour variable names are accepted.
|
||||||
|
Values must match `\$[A-Za-z_][A-Za-z0-9_]*` or `\\e\[[0-9;]*m`; any other
|
||||||
|
value is discarded with a warning.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> **Note:** Versions prior to `3.95.x-4_beta` did not maintain a formal
|
||||||
|
> changelog. The full history of earlier changes is available through the git
|
||||||
|
> log (`git log --oneline`).
|
||||||
|
|
||||||
|
## [3.95.3-4_beta_3] — 2024
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Initial public release candidate series.
|
||||||
|
- Core modules: `compress`, `disp`, `filefct`, `fun`, `help`, `info`, `lang`,
|
||||||
|
`net`, `packages`, `processes`, `prompt`, `pwd`, `rain`, `ssh`, `updates`.
|
||||||
|
- Bar-style prompt with time, command duration, and exit code.
|
||||||
|
- `taz` / `utaz` archive helpers.
|
||||||
|
- `genpwd` / `pwdscore` password tools.
|
||||||
|
- `matrix` / `rain` screensavers.
|
||||||
|
- `profile_upgrade` with git and archive download support.
|
||||||
210
doc/CONTRIBUTING.md
Executable file
210
doc/CONTRIBUTING.md
Executable file
@@ -0,0 +1,210 @@
|
|||||||
|
# Contributing to profile
|
||||||
|
|
||||||
|
Thank you for your interest in contributing. This document explains how to get
|
||||||
|
set up, what the conventions are, and how to submit work.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Before you start
|
||||||
|
|
||||||
|
- Check the [to-do list](./todo.md) to see if your idea is already planned.
|
||||||
|
- Check the [issue tracker](https://git.geoffray-levasseur.org/fatalerrors/profile/issues)
|
||||||
|
to avoid duplicate work.
|
||||||
|
- For significant changes, open an issue or contact the maintainer before
|
||||||
|
writing code — alignment on design saves everyone time.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Getting the source
|
||||||
|
|
||||||
|
A Git clone is mandatory for contributions:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://git.geoffray-levasseur.org/fatalerrors/profile.git
|
||||||
|
cd profile
|
||||||
|
```
|
||||||
|
|
||||||
|
Always work from the **latest commit on `master`** (or the branch you intend
|
||||||
|
to target). Stale forks cause avoidable merge conflicts.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Branch policy
|
||||||
|
|
||||||
|
| 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 |
|
||||||
|
| git | any | For contributing patches |
|
||||||
|
| bats-core | 1.x | Optional — for running the test suite |
|
||||||
|
|
||||||
|
Install shellcheck:
|
||||||
|
```bash
|
||||||
|
# Debian / Ubuntu
|
||||||
|
apt-get install shellcheck
|
||||||
|
|
||||||
|
# Fedora / RHEL
|
||||||
|
dnf install ShellCheck
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
brew install shellcheck
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Code style
|
||||||
|
|
||||||
|
### General rules
|
||||||
|
- **Bash only** — no external interpreters in core modules. Python or Perl is
|
||||||
|
acceptable for completely self-contained, optional utilities that have no
|
||||||
|
dependencies beyond a minimal Debian or CentOS installation.
|
||||||
|
- **4-space indentation** — no tabs.
|
||||||
|
- **`[[ … ]]`** for all conditionals — not `[ … ]`.
|
||||||
|
- **`(( … ))`** for arithmetic — not `$(( … ))` in conditionals.
|
||||||
|
- **`local`** for all function-internal variables — avoid polluting the
|
||||||
|
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
|
||||||
|
`${VAR:-default}` and document the key in `profile.conf` and `README.md §4`.
|
||||||
|
|
||||||
|
### Function conventions
|
||||||
|
- Public functions **must** be exported: `export -f funcname`.
|
||||||
|
- Every public function **must** support `-h` / `--help` and print usage to
|
||||||
|
stdout, returning 0.
|
||||||
|
- Use `getopt` (not `getopts`) for option parsing — it handles long options and
|
||||||
|
`--` correctly.
|
||||||
|
- Follow existing error-return conventions: 0 = success, 1 = usage error,
|
||||||
|
2 = bad options, 3 = missing dependency, 4+ = runtime failure.
|
||||||
|
- Prefix all local helper variables with a short unique prefix (e.g. `_taz_`)
|
||||||
|
to prevent collisions with caller-scope variables.
|
||||||
|
|
||||||
|
### Module structure
|
||||||
|
Every new module should follow this pattern:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# <copyright block identical to existing modules>
|
||||||
|
|
||||||
|
load_conf "<module_name>"
|
||||||
|
|
||||||
|
# --- functions ---
|
||||||
|
|
||||||
|
export -f my_function
|
||||||
|
|
||||||
|
# EOF
|
||||||
|
```
|
||||||
|
|
||||||
|
Add the `load_conf` call near the top after any variable declarations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Configuration keys
|
||||||
|
|
||||||
|
When adding a configurable default:
|
||||||
|
|
||||||
|
1. Use `${MY_VAR:-hardcoded_default}` in the function body.
|
||||||
|
2. Add a commented-out entry with a description to `profile.conf`.
|
||||||
|
3. Document the key in the matching table in `README.md §4.2`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Theming
|
||||||
|
|
||||||
|
New theme files go in `profile.d/themes/` with a `.theme` extension.
|
||||||
|
They are **parsed, not executed** — do not add shell logic.
|
||||||
|
See the existing themes and `README.md §4.4` for the allowed syntax.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Running shellcheck
|
||||||
|
|
||||||
|
```bash
|
||||||
|
shellcheck -x profile.sh profile.d/*.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
All warnings must be resolved before a patch will be accepted. Accepted
|
||||||
|
suppression directives (`# shellcheck disable=SCxxxx`) require an inline
|
||||||
|
comment explaining why the suppression is necessary.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. Submitting a contribution
|
||||||
|
|
||||||
|
### Via Git (preferred)
|
||||||
|
1. Contact the maintainer to obtain push access, or fork on the Gitea instance.
|
||||||
|
2. Create a branch: `git checkout -b feature/my-feature`.
|
||||||
|
3. Commit with a clear subject line: `module: short description (≤ 72 chars)`.
|
||||||
|
4. Push and open a pull request against `master`.
|
||||||
|
|
||||||
|
### Via patch
|
||||||
|
If you do not have push access:
|
||||||
|
```bash
|
||||||
|
git format-patch origin/master
|
||||||
|
```
|
||||||
|
Send the resulting `.patch` file(s) to
|
||||||
|
`fatalerrors <at> geoffray-levasseur <dot> org`.
|
||||||
|
|
||||||
|
### Commit message format
|
||||||
|
```
|
||||||
|
module: imperative short description
|
||||||
|
|
||||||
|
Optional longer explanation of what changed and why. Wrap at 72 characters.
|
||||||
|
Reference issue numbers if applicable: closes #42.
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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.
|
||||||
|
- Changes that break Bash 4.3 compatibility.
|
||||||
|
- Patches without a passing `shellcheck` run.
|
||||||
|
- New functions without `--help` support.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12. Financial contributions
|
||||||
|
|
||||||
|
Contact the maintainer by mail if you wish to make a financial contribution.
|
||||||
208
doc/FAQ.md
Executable file
208
doc/FAQ.md
Executable file
@@ -0,0 +1,208 @@
|
|||||||
|
# Frequently Asked Questions
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installation & loading
|
||||||
|
|
||||||
|
**Q: profile refuses to load and prints "This profile requires Bash 4.3 or higher."**
|
||||||
|
|
||||||
|
Your system's default shell is an older Bash (common on macOS, which ships
|
||||||
|
Bash 3.x for licensing reasons). Install a newer Bash:
|
||||||
|
```bash
|
||||||
|
# macOS
|
||||||
|
brew install bash
|
||||||
|
# then add /opt/homebrew/bin/bash to /etc/shells and chsh
|
||||||
|
```
|
||||||
|
Or point your terminal emulator at the newer binary explicitly.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Q: I sourced `profile.sh` but functions are not available in sub-shells or scripts.**
|
||||||
|
|
||||||
|
All public functions are exported with `export -f`. They are available in
|
||||||
|
child Bash processes, but **not** in POSIX `sh` sub-shells. Make sure your
|
||||||
|
scripts start with `#!/usr/bin/env bash`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Q: I set `PROFILE_PATH` but profile still can't find its modules.**
|
||||||
|
|
||||||
|
`PROFILE_PATH` must be exported *before* you source `profile.sh`:
|
||||||
|
```bash
|
||||||
|
export PROFILE_PATH=/opt/profile
|
||||||
|
source /opt/profile/profile.sh
|
||||||
|
```
|
||||||
|
If set after sourcing, `MYPATH` is already locked in and the variable has
|
||||||
|
no effect.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Q: Can I load profile system-wide via `/etc/profile`?**
|
||||||
|
|
||||||
|
It is not recommended. User `.bashrc` files frequently set variables that
|
||||||
|
conflict with the aliases and locale functions defined here, leading to
|
||||||
|
surprising behaviour. Per-user sourcing from `~/.bashrc` is the supported
|
||||||
|
method.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
**Q: I edited `profile.conf` but my changes have no effect.**
|
||||||
|
|
||||||
|
`profile.conf` is parsed once per shell session at load time. Open a new
|
||||||
|
terminal (or `exec bash`) to pick up the changes. There is no live-reload.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Q: How do I find out which configuration keys a module supports?**
|
||||||
|
|
||||||
|
Every supported key is documented with a comment in `profile.conf`.
|
||||||
|
See also `README.md §4.2` for a consolidated table.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Q: A key I set in `profile.conf` is being ignored.**
|
||||||
|
|
||||||
|
Check that:
|
||||||
|
1. The key is inside the correct `[section]` header.
|
||||||
|
2. There is no leading space before the section name (`[section]` not
|
||||||
|
`[ section ]`).
|
||||||
|
3. The key is not commented out (no leading `#`).
|
||||||
|
4. The value contains no backticks or `$(…)` — these are stripped by the
|
||||||
|
parser as a security measure.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt & theming
|
||||||
|
|
||||||
|
**Q: How do I change the prompt theme?**
|
||||||
|
|
||||||
|
Add to `profile.conf`:
|
||||||
|
```ini
|
||||||
|
[prompt]
|
||||||
|
PROMPT_THEME = dark
|
||||||
|
```
|
||||||
|
Built-in names: `default`, `dark`, `light`, `solarized`, `solarized-light`,
|
||||||
|
`monokai`, `monochrome`, `abyss`, `plasma`, `adwaita`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Q: The solarized or solarized-light theme shows wrong colours.**
|
||||||
|
|
||||||
|
Those themes use 24-bit / true-colour ANSI sequences (`\e[38;2;R;G;Bm`).
|
||||||
|
Test your terminal:
|
||||||
|
```bash
|
||||||
|
printf '\e[38;2;38;139;210mTrue colour test\e[0m\n'
|
||||||
|
```
|
||||||
|
If you see a solid blue word your terminal supports true colour.
|
||||||
|
If you see garbage or plain text, switch to a 16-colour theme
|
||||||
|
(`dark`, `default`, etc.) or upgrade your terminal emulator.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Q: I created a custom theme but `load_theme` emits "key not allowed" warnings.**
|
||||||
|
|
||||||
|
Theme files are parsed, not executed. Only `PROMPT_COLOR_*` keys and the
|
||||||
|
standard colour variable names from `disp.sh` (`Black`, `Blue`, `On_IBlack`,
|
||||||
|
…) are accepted. Any other key — including custom variables — is rejected.
|
||||||
|
See `README.md §4.4` for the full list of accepted keys and value forms.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Q: Can a theme file contain shell logic or `$(…)` command substitutions?**
|
||||||
|
|
||||||
|
No, and intentionally so. Theme files are parsed line-by-line; shell
|
||||||
|
constructs are never evaluated. This is a security boundary — a malicious
|
||||||
|
theme file cannot execute code. Values must be a colour variable reference
|
||||||
|
(`$Blue`) or a raw ANSI escape literal (`\e[0;34m`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Functions
|
||||||
|
|
||||||
|
**Q: `meteo` prints "No city specified" even though I set a default.**
|
||||||
|
|
||||||
|
The key is `METEO_DEFAULT_CITY` (not `DEFAULT_CITY`), and it must be in the
|
||||||
|
`[info]` section:
|
||||||
|
```ini
|
||||||
|
[info]
|
||||||
|
METEO_DEFAULT_CITY = Paris
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Q: `dwl` fails with "no download tool found".**
|
||||||
|
|
||||||
|
`dwl` requires one of `curl`, `wget`, or `fetch` to be installed.
|
||||||
|
Install curl:
|
||||||
|
```bash
|
||||||
|
# Debian / Ubuntu
|
||||||
|
apt-get install curl
|
||||||
|
|
||||||
|
# Fedora / RHEL
|
||||||
|
dnf install curl
|
||||||
|
```
|
||||||
|
Or set `DWL_PREFERRED_TOOL` in `[net]` to whichever tool you have.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Q: `pkgs` does not find packages I know are installed.**
|
||||||
|
|
||||||
|
`pkgs` delegates to `dpkg -l` (Debian/Ubuntu) or `rpm -qa` (RHEL/Fedora).
|
||||||
|
If your distribution uses a different package manager (pacman, apk, brew …)
|
||||||
|
it is not yet supported. See `doc/todo.md` for the tracking issue.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Q: `profile_upgrade` says "no update available" but I know there is one.**
|
||||||
|
|
||||||
|
`check_updates` compares the content of the remote `version` file against
|
||||||
|
`$PROFVERSION`. If `UPDT_DEFAULT_BRANCH` in `[updates]` points to a different
|
||||||
|
branch than your installation, the version files may not match. Check:
|
||||||
|
```bash
|
||||||
|
cat "$MYPATH/version"
|
||||||
|
```
|
||||||
|
and make sure `UPDT_DEFAULT_BRANCH` matches the branch you track.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Compatibility
|
||||||
|
|
||||||
|
**Q: Some functions misbehave on macOS / Cygwin.**
|
||||||
|
|
||||||
|
Both environments ship non-GNU userland utilities with different flags and
|
||||||
|
behaviour. profile is primarily developed and tested on Linux (Debian and
|
||||||
|
RHEL families). macOS and Cygwin bugs are low priority; patches that add
|
||||||
|
compatibility without breaking Linux support are welcome.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Q: Can I use profile with ZSH?**
|
||||||
|
|
||||||
|
Not officially. Blockers include `local -A` (ZSH requires `typeset -A`) and
|
||||||
|
`local -n` namerefs. A compatibility layer is listed in `doc/todo.md` but
|
||||||
|
has not been implemented yet.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Miscellaneous
|
||||||
|
|
||||||
|
**Q: How do I completely disable profile for one session?**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
PROFILE_DISABLED=1 bash --norc
|
||||||
|
```
|
||||||
|
Or simply open a shell without sourcing `~/.bashrc` (`bash --norc`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Q: How do I report a bug?**
|
||||||
|
|
||||||
|
Open an issue on the
|
||||||
|
[Gitea tracker](https://git.geoffray-levasseur.org/fatalerrors/profile/issues)
|
||||||
|
or send a mail to `fatalerrors <at> geoffray-levasseur <dot> org` with:
|
||||||
|
- The exact command that triggered the bug
|
||||||
|
- Your OS and Bash version (`bash --version`)
|
||||||
|
- The module involved
|
||||||
|
- Any relevant error output
|
||||||
29
doc/LICENSE
Executable file
29
doc/LICENSE
Executable 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.
|
||||||
252
doc/profile.conf.example
Executable file
252
doc/profile.conf.example
Executable file
@@ -0,0 +1,252 @@
|
|||||||
|
# profile.conf — example / reference configuration
|
||||||
|
# Copy this file to the profile installation directory as "profile.conf"
|
||||||
|
# and uncomment / edit the keys you want to change.
|
||||||
|
#
|
||||||
|
# Format:
|
||||||
|
# [section_name] — starts a section
|
||||||
|
# key = value — sets a key (whitespace around = is optional)
|
||||||
|
# # comment — line comment
|
||||||
|
#
|
||||||
|
# All keys are optional. Sensible defaults apply when unset.
|
||||||
|
# Values must not contain backticks or $(…) — those are stripped for security.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
[system]
|
||||||
|
# Bash history settings
|
||||||
|
HISTSIZE=50000
|
||||||
|
HISTFILESIZE=100000
|
||||||
|
HISTIGNORE="&:[bf]g:exit"
|
||||||
|
|
||||||
|
# Default pager
|
||||||
|
PAGER=less
|
||||||
|
|
||||||
|
# Terminal colour capability
|
||||||
|
TERM=xterm-256color
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
[compress]
|
||||||
|
# taz: Default archive format (-f/--format).
|
||||||
|
# Supported: lz (default), xz, bz2, gz, lzo, tar, zip, zst
|
||||||
|
#TAZ_DEFAULT_FORMAT=lz
|
||||||
|
|
||||||
|
# taz: Number of compression threads (0 = auto-detect CPU count).
|
||||||
|
#TAZ_DEFAULT_THREADS=0
|
||||||
|
|
||||||
|
# taz: Compression level 1 (fast/large) … 9 (slow/small).
|
||||||
|
#TAZ_DEFAULT_LEVEL=6
|
||||||
|
|
||||||
|
# utaz: Delete source archive after successful extraction (0=no, 1=yes).
|
||||||
|
#UTAZ_DEFAULT_DELETE=0
|
||||||
|
|
||||||
|
# utaz: Subdirectory creation policy.
|
||||||
|
# auto — create one only when the archive has multiple top-level entries
|
||||||
|
# always — always extract into a new subdirectory
|
||||||
|
# never — always flatten into the current directory
|
||||||
|
#UTAZ_DEFAULT_DIR_MODE=auto
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
[debug]
|
||||||
|
# (no configurable keys yet)
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
[disp]
|
||||||
|
# Uncomment to disable ANSI colours in profile's own output messages.
|
||||||
|
#NO_COLOR=1
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
[filefct]
|
||||||
|
# expandlist: Separator between items (default: space). Use \n for newline.
|
||||||
|
#EXPANDLIST_DEFAULT_SEPARATOR=" "
|
||||||
|
|
||||||
|
# clean: Recurse into subdirectories by default (0=no, 1=yes).
|
||||||
|
#CLEAN_DEFAULT_RECURSIVE=0
|
||||||
|
|
||||||
|
# rmspc: Replacement character for spaces in filenames (default: underscore).
|
||||||
|
#RMSPC_DEFAULT_CHAR=_
|
||||||
|
|
||||||
|
# findbig: Number of results to display (default: 10).
|
||||||
|
#FINDBIG_DEFAULT_LIMIT=10
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
[fun]
|
||||||
|
# busy: Hex pattern searched in /dev/urandom hexdump.
|
||||||
|
#BUSY_DEFAULT_PATTERN=ca fe
|
||||||
|
|
||||||
|
# busy: Delay between matched lines in milliseconds (0 = no delay).
|
||||||
|
#BUSY_DEFAULT_DELAY=0
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
[info]
|
||||||
|
# meteo: Default city when no argument is given. Leave unset to require an
|
||||||
|
# explicit city argument every time.
|
||||||
|
#METEO_DEFAULT_CITY=Paris
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
[lang]
|
||||||
|
# Comma-separated alias:locale pairs. One function is generated per entry.
|
||||||
|
# Example: SET_LOCALE="fr:fr_FR.UTF-8,us:en_US.UTF-8,es:es_ES.UTF-8"
|
||||||
|
# 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).
|
||||||
|
# Unset = auto-detect (curl preferred, then wget, then fetch).
|
||||||
|
#DWL_PREFERRED_TOOL=curl
|
||||||
|
|
||||||
|
# myextip: API endpoint for external IP lookup.
|
||||||
|
# Alternatives: https://ipinfo.io/json, https://ip-api.com/json/
|
||||||
|
#MYEXTIP_DEFAULT_URL=https://ip-api.com/json/
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
[packages]
|
||||||
|
# pkgs: Case-insensitive search by default (0=no, 1=yes).
|
||||||
|
#PKGS_DEFAULT_IGNORE_CASE=0
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
[processes]
|
||||||
|
# ppu: ps output columns (comma-separated ps field names).
|
||||||
|
#PPU_DEFAULT_FORMAT=pid,user,%cpu,%mem,start,time,command
|
||||||
|
|
||||||
|
# ku: Signal sent when killing a user's processes (without SIG prefix).
|
||||||
|
#KU_DEFAULT_SIGNAL=TERM
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
[prompt]
|
||||||
|
# Name of the prompt theme to load, or an explicit path to a .theme file.
|
||||||
|
# Built-in themes: default, dark, light, solarized, solarized-light,
|
||||||
|
# monokai, monochrome, abyss, plasma, adwaita
|
||||||
|
# Solarized variants require a terminal with 24-bit true-colour support.
|
||||||
|
#PROMPT_THEME=default
|
||||||
|
|
||||||
|
# Directory searched for bare theme names. Defaults to profile.d/themes/.
|
||||||
|
#PROMPT_THEME_DIR=/path/to/my/themes
|
||||||
|
|
||||||
|
# Individual colour overrides — these always win over the loaded theme.
|
||||||
|
# Values must be colour variable names from disp.sh (e.g. $Blue, $On_IBlack)
|
||||||
|
# or raw ANSI escape sequences (e.g. \e[0;34m).
|
||||||
|
#
|
||||||
|
# Clock segment
|
||||||
|
#PROMPT_COLOR_TIME_FG=$Blue
|
||||||
|
#PROMPT_COLOR_TIME_BG=$On_IBlack
|
||||||
|
#
|
||||||
|
# Main bar background
|
||||||
|
#PROMPT_COLOR_BAR_BG=$On_Blue
|
||||||
|
#
|
||||||
|
# Exit-code segment — success
|
||||||
|
#PROMPT_COLOR_OK_FG=$White
|
||||||
|
#PROMPT_COLOR_OK_MARK=$Green
|
||||||
|
#
|
||||||
|
# Exit-code segment — failure
|
||||||
|
#PROMPT_COLOR_ERR_BG=$On_Red
|
||||||
|
#PROMPT_COLOR_ERR_FG=$White
|
||||||
|
#PROMPT_COLOR_ERR_MARK=$BYellow
|
||||||
|
#
|
||||||
|
# User / host
|
||||||
|
#PROMPT_COLOR_ROOT_FG=$Red
|
||||||
|
#PROMPT_COLOR_USER_FG=$Green
|
||||||
|
#
|
||||||
|
# Working directory
|
||||||
|
#PROMPT_COLOR_DIR_FG=$ICyan
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
[pwd]
|
||||||
|
# genpwd: Default password length.
|
||||||
|
#GENPWD_DEFAULT_LENGTH=16
|
||||||
|
|
||||||
|
# genpwd: Maximum occurrences of any single character.
|
||||||
|
#GENPWD_DEFAULT_OCCURS=2
|
||||||
|
|
||||||
|
# genpwd: Number of passwords generated per invocation.
|
||||||
|
#GENPWD_DEFAULT_COUNT=1
|
||||||
|
|
||||||
|
# genpwd: Include symbols (1=yes, 0=no).
|
||||||
|
#GENPWD_DEFAULT_SYMBOLS=1
|
||||||
|
|
||||||
|
# genpwd: Include uppercase letters (1=yes, 0=no).
|
||||||
|
#GENPWD_DEFAULT_UPPERCASE=1
|
||||||
|
|
||||||
|
# genpwd: Include lowercase letters (1=yes, 0=no).
|
||||||
|
#GENPWD_DEFAULT_LOWERCASE=1
|
||||||
|
|
||||||
|
# genpwd: Include digits (1=yes, 0=no).
|
||||||
|
#GENPWD_DEFAULT_NUMBERS=1
|
||||||
|
|
||||||
|
# pwdscore: Show verbose breakdown by default (1=yes, 0=no).
|
||||||
|
#PWDSCORE_DEFAULT_VERBOSE=0
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
[rain]
|
||||||
|
# rain: Falling speed — integer/100 gives seconds (5 → 0.05 s).
|
||||||
|
# Values < 1 are used as raw seconds.
|
||||||
|
#RAIN_DEFAULT_SPEED=5
|
||||||
|
|
||||||
|
# rain: Colour theme. Supported: white (default), green, blue, red, yellow, cyan
|
||||||
|
#RAIN_DEFAULT_COLOR=white
|
||||||
|
|
||||||
|
# matrix: Falling speed.
|
||||||
|
#MATRIX_DEFAULT_SPEED=3.5
|
||||||
|
|
||||||
|
# matrix: Colour theme. Supported: green (default), blue, red, yellow, cyan, white
|
||||||
|
#MATRIX_DEFAULT_COLOR=green
|
||||||
|
|
||||||
|
# matrix: Character set. Supported: binary (default), kana, ascii
|
||||||
|
#MATRIX_DEFAULT_CHARSET=binary
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
[ssh]
|
||||||
|
# ssr: Default SSH options prepended to every ssr call.
|
||||||
|
# Examples:
|
||||||
|
# SSH_DEFAULT_OPT=-Y # X11 forwarding
|
||||||
|
# SSH_DEFAULT_OPT=-Y -o StrictHostKeyChecking=accept-new
|
||||||
|
# SSH_DEFAULT_OPT= # no default options
|
||||||
|
#SSH_DEFAULT_OPT=-Y
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
[updates]
|
||||||
|
# Git branch used for update checks and profile_upgrade.
|
||||||
|
# Changing this causes profile_upgrade to automatically switch the local
|
||||||
|
# checkout to the new branch on the next run and display a warning.
|
||||||
|
#UPDT_DEFAULT_BRANCH=master
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
[general]
|
||||||
|
# General-purpose section — set any environment variable you need globally.
|
||||||
|
# Good place for compilation flags, personal PATH additions, etc.
|
||||||
|
#CFLAGS=-O2 -pipe -march=native
|
||||||
|
#CXXFLAGS=$CFLAGS
|
||||||
|
#MAKEFLAGS=-j4
|
||||||
|
#PKGSOURCES=/usr/local/src
|
||||||
|
|
||||||
|
# ==============================================================================
|
||||||
|
[aliases]
|
||||||
|
# Command aliases loaded for interactive shells only.
|
||||||
|
# The value is the full command string; the key becomes the alias name.
|
||||||
|
# The base command must be executable; if not, the alias is silently skipped.
|
||||||
|
#
|
||||||
|
# ls
|
||||||
|
ll=ls -laFh --color=auto
|
||||||
|
la=ls -Ah --color=auto
|
||||||
|
l=ls -CF --color=auto
|
||||||
|
ls=ls --color=auto
|
||||||
|
#
|
||||||
|
# grep
|
||||||
|
grep=grep --color=auto
|
||||||
|
egrep=egrep --color=auto
|
||||||
|
fgrep=fgrep --color=auto
|
||||||
|
#
|
||||||
|
# disk usage
|
||||||
|
df=df -H
|
||||||
|
du=du -ch
|
||||||
|
#
|
||||||
|
# make shortcuts
|
||||||
|
#mk=make
|
||||||
|
#mkin=make install
|
||||||
|
|
||||||
|
# End of profile.conf.example
|
||||||
274
doc/profile.conf.fatalerrors
Executable file
274
doc/profile.conf.fatalerrors
Executable file
@@ -0,0 +1,274 @@
|
|||||||
|
[system]
|
||||||
|
# System section is used to set Bash behavior and other system related
|
||||||
|
# variables, such as the default pager, the terminal type, etc.
|
||||||
|
# Set bash history
|
||||||
|
HISTSIZE=50000
|
||||||
|
HISTIGNORE="&:[bf]g:exit"
|
||||||
|
|
||||||
|
# Set default pager
|
||||||
|
PAGER=less
|
||||||
|
|
||||||
|
# Set terminal colors behavior
|
||||||
|
TERM=xterm-256color
|
||||||
|
|
||||||
|
[compress]
|
||||||
|
# Section used by compress.sh (taz and utaz functions).
|
||||||
|
|
||||||
|
# taz: Default archive format when -f/--format is not specified.
|
||||||
|
# Supported values: lz (default), xz, bz2, gz, lzo, tar
|
||||||
|
#TAZ_DEFAULT_FORMAT=lz
|
||||||
|
|
||||||
|
# taz: Default number of compression threads when -p/--parallel is not specified.
|
||||||
|
# Set to the number of CPU cores to use parallel compression where supported.
|
||||||
|
#TAZ_DEFAULT_THREADS=1
|
||||||
|
|
||||||
|
# taz: Default compression level when none is given (1=fast/big .. 9=slow/small).
|
||||||
|
#TAZ_DEFAULT_LEVEL=6
|
||||||
|
|
||||||
|
# utaz: Delete source archives after successful extraction (0=no, 1=yes).
|
||||||
|
#UTAZ_DEFAULT_DELETE=0
|
||||||
|
|
||||||
|
# utaz: Default directory creation mode when neither --create-dir nor --no-dir is given.
|
||||||
|
# Supported values:
|
||||||
|
# auto (default) — create a subdirectory only when the archive contains
|
||||||
|
# multiple top-level entries or a bare file
|
||||||
|
# always — always extract into a new subdirectory
|
||||||
|
# never — always flatten extraction into the current directory
|
||||||
|
#UTAZ_DEFAULT_DIR_MODE=auto
|
||||||
|
|
||||||
|
[debug]
|
||||||
|
# Section used by debug.sh (nothing yet)
|
||||||
|
|
||||||
|
[disp]
|
||||||
|
# Section used by disp.sh
|
||||||
|
|
||||||
|
# Set to any value to disable colors in internal profile output (not controling binary output)
|
||||||
|
# NO_COLOR=1
|
||||||
|
|
||||||
|
[filefct]
|
||||||
|
# Section used by filefct.sh
|
||||||
|
|
||||||
|
# expandlist: Default output separator between items (default: space).
|
||||||
|
# Use \n for newline, or any other character/string.
|
||||||
|
#EXPANDLIST_DEFAULT_SEPARATOR=" "
|
||||||
|
|
||||||
|
# clean: Enable recursive mode by default (0=no, 1=yes).
|
||||||
|
#CLEAN_DEFAULT_RECURSIVE=0
|
||||||
|
|
||||||
|
# rmspc: Default character used to replace spaces in filenames (default: underscore).
|
||||||
|
# Set to empty to concatenate words without separator (equivalent to --subst-char with no value).
|
||||||
|
#RMSPC_DEFAULT_CHAR=_
|
||||||
|
|
||||||
|
# findbig: Default number of results to return (default: 10).
|
||||||
|
#FINDBIG_DEFAULT_LIMIT=10
|
||||||
|
|
||||||
|
[fun]
|
||||||
|
# Section used by fun.sh (busy function).
|
||||||
|
|
||||||
|
# busy: Default hex pattern to search for in /dev/urandom hexdump (default: "ca fe").
|
||||||
|
#BUSY_DEFAULT_PATTERN=ca fe
|
||||||
|
|
||||||
|
# busy: Default delay between matched lines in milliseconds (default: 0 = no delay).
|
||||||
|
#BUSY_DEFAULT_DELAY=0
|
||||||
|
|
||||||
|
[info]
|
||||||
|
# Section used by info.sh
|
||||||
|
|
||||||
|
# meteo: Default city used when no city argument is given.
|
||||||
|
# Unset or empty disables the fallback and requires an explicit city argument.
|
||||||
|
METEO_DEFAULT_CITY="Toulouse"
|
||||||
|
|
||||||
|
[lang]
|
||||||
|
# Section used by lang.sh
|
||||||
|
# List of locale shortcuts to build, in the form "shortcut:locale,...".
|
||||||
|
# Generate a function setXX for each shortcut defined.
|
||||||
|
SET_LOCALE="fr:fr_FR.UTF-8,us:en_US.UTF-8"
|
||||||
|
|
||||||
|
[net]
|
||||||
|
# Section used by net.sh (dwl, myextip functions).
|
||||||
|
|
||||||
|
# dwl: Force a specific download tool instead of auto-detecting (curl → wget → fetch).
|
||||||
|
# Supported values: curl, wget, fetch. Unset uses auto-detection (default).
|
||||||
|
#DWL_PREFERRED_TOOL=curl
|
||||||
|
|
||||||
|
# myextip: API endpoint URL used to retrieve external IP information.
|
||||||
|
# Default: https://ip-api.com/json/
|
||||||
|
# Compatible alternatives: https://ipinfo.io/json, https://ip-api.com/json/
|
||||||
|
#MYEXTIP_DEFAULT_URL=https://ip-api.com/json/
|
||||||
|
|
||||||
|
[packages]
|
||||||
|
# Section used by packages.sh
|
||||||
|
|
||||||
|
# pkgs: Enable case-insensitive search by default (1=yes, 0=no; default: 0).
|
||||||
|
#PKGS_DEFAULT_IGNORE_CASE=0
|
||||||
|
|
||||||
|
[processes]
|
||||||
|
# Section used by processes.sh
|
||||||
|
|
||||||
|
# ppu: Output columns passed to ps -o when listing processes for a user.
|
||||||
|
# Comma-separated list of ps field names. Default: pid,user,%cpu,%mem,start,time,command
|
||||||
|
#PPU_DEFAULT_FORMAT=pid,user,%cpu,%mem,start,time,command
|
||||||
|
|
||||||
|
# ku: Signal sent to processes when killing a user's session (default: TERM).
|
||||||
|
# Use signal names without the SIG prefix (e.g. TERM, KILL, HUP).
|
||||||
|
#KU_DEFAULT_SIGNAL=TERM
|
||||||
|
|
||||||
|
[prompt]
|
||||||
|
# Section used by prompt.sh
|
||||||
|
|
||||||
|
# Name of the theme to load, or an explicit path to a .theme file.
|
||||||
|
# Bare names are resolved as $PROMPT_THEME_DIR/<name>.theme.
|
||||||
|
# When unset, no theme is loaded and the hardcoded fallback colours are used.
|
||||||
|
#PROMPT_THEME=default
|
||||||
|
|
||||||
|
# Directory that contains .theme files. Defaults to profile.d/themes/ inside
|
||||||
|
# the profile installation directory.
|
||||||
|
#PROMPT_THEME_DIR=/path/to/themes
|
||||||
|
|
||||||
|
# Individual colour overrides. These always win over the loaded theme.
|
||||||
|
# Values must be valid ANSI escape sequences as exported by disp.sh, e.g.:
|
||||||
|
# \e[0;34m (Blue) \e[42m (On_Green) \e[1;32m (BGreen)
|
||||||
|
# All keys listed below correspond to variables exported by disp.sh.
|
||||||
|
|
||||||
|
# Clock segment
|
||||||
|
#PROMPT_COLOR_TIME_FG=$Blue
|
||||||
|
#PROMPT_COLOR_TIME_BG=$On_IBlack
|
||||||
|
|
||||||
|
# Main bar background (success and info)
|
||||||
|
#PROMPT_COLOR_BAR_BG=$On_Blue
|
||||||
|
|
||||||
|
# Exit-code segment — success state
|
||||||
|
#PROMPT_COLOR_OK_FG=$White
|
||||||
|
#PROMPT_COLOR_OK_MARK=$Green
|
||||||
|
|
||||||
|
# Exit-code segment — failure state
|
||||||
|
#PROMPT_COLOR_ERR_BG=$On_Red
|
||||||
|
#PROMPT_COLOR_ERR_FG=$White
|
||||||
|
#PROMPT_COLOR_ERR_MARK=$BYellow
|
||||||
|
|
||||||
|
# User/host colours
|
||||||
|
#PROMPT_COLOR_ROOT_FG=$Red
|
||||||
|
#PROMPT_COLOR_USER_FG=$Green
|
||||||
|
|
||||||
|
# Working directory
|
||||||
|
#PROMPT_COLOR_DIR_FG=$ICyan
|
||||||
|
|
||||||
|
[pwd]
|
||||||
|
# Section used by pwd.sh (genpwd and pwdscore functions).
|
||||||
|
|
||||||
|
# genpwd: Default password length (default: 16).
|
||||||
|
#GENPWD_DEFAULT_LENGTH=16
|
||||||
|
|
||||||
|
# genpwd: Maximum occurrences of any single character (default: 2).
|
||||||
|
#GENPWD_DEFAULT_OCCURS=2
|
||||||
|
|
||||||
|
# genpwd: Number of passwords generated when no count argument is given (default: 1).
|
||||||
|
#GENPWD_DEFAULT_COUNT=1
|
||||||
|
|
||||||
|
# genpwd: Include symbols in the character pool (1=yes, 0=no; default: 1).
|
||||||
|
#GENPWD_DEFAULT_SYMBOLS=1
|
||||||
|
|
||||||
|
# genpwd: Include uppercase letters in the character pool (1=yes, 0=no; default: 1).
|
||||||
|
#GENPWD_DEFAULT_UPPERCASE=1
|
||||||
|
|
||||||
|
# genpwd: Include lowercase letters in the character pool (1=yes, 0=no; default: 1).
|
||||||
|
#GENPWD_DEFAULT_LOWERCASE=1
|
||||||
|
|
||||||
|
# genpwd: Include digits in the character pool (1=yes, 0=no; default: 1).
|
||||||
|
#GENPWD_DEFAULT_NUMBERS=1
|
||||||
|
|
||||||
|
# pwdscore: Show verbose breakdown by default (1=yes, 0=no; default: 0).
|
||||||
|
#PWDSCORE_DEFAULT_VERBOSE=0
|
||||||
|
|
||||||
|
[rain]
|
||||||
|
# Section used by rain.sh (rain and matrix functions).
|
||||||
|
|
||||||
|
# rain: Default speed value, using the /100 scale (5 => 0.050s, 10 => 0.100s).
|
||||||
|
# Values < 1 are interpreted as raw seconds.
|
||||||
|
#RAIN_DEFAULT_SPEED=5
|
||||||
|
|
||||||
|
# rain: Default color theme.
|
||||||
|
# Supported values: white (default), green, blue, red, yellow, cyan
|
||||||
|
#RAIN_DEFAULT_COLOR=white
|
||||||
|
|
||||||
|
# matrix: Default speed value, using the /100 scale (3.5 => 0.035s).
|
||||||
|
#MATRIX_DEFAULT_SPEED=3.5
|
||||||
|
|
||||||
|
# matrix: Default color theme.
|
||||||
|
# Supported values: green (default), blue, red, yellow, cyan, white
|
||||||
|
#MATRIX_DEFAULT_COLOR=green
|
||||||
|
|
||||||
|
# matrix: Default character set.
|
||||||
|
# Supported values: binary (default), kana, ascii
|
||||||
|
MATRIX_DEFAULT_CHARSET=kana
|
||||||
|
|
||||||
|
[ssh]
|
||||||
|
# Section used by ssh.sh
|
||||||
|
|
||||||
|
# ssr: Default SSH options prepended to every ssr invocation.
|
||||||
|
# Options are word-split, so space-separated flags are supported.
|
||||||
|
# The default behaviour without this key is equivalent to: SSH_DEFAULT_OPT=-Y
|
||||||
|
# Set to an empty value to pass no default options.
|
||||||
|
# Examples:
|
||||||
|
# SSH_DEFAULT_OPT=-Y # X11 forwarding (original default)
|
||||||
|
# SSH_DEFAULT_OPT=-Y -o StrictHostKeyChecking=accept-new
|
||||||
|
# SSH_DEFAULT_OPT= # no default options
|
||||||
|
#SSH_DEFAULT_OPT=-Y
|
||||||
|
|
||||||
|
[updates]
|
||||||
|
# Section used by updates.sh
|
||||||
|
#
|
||||||
|
# UPDT_DEFAULT_BRANCH — Git branch used for update checks and upgrades.
|
||||||
|
# Defaults to 'master' when unset. Changing this value will cause
|
||||||
|
# profile_upgrade to automatically switch the local checkout to the new
|
||||||
|
# branch on the next upgrade and display a warning.
|
||||||
|
#UPDT_DEFAULT_BRANCH=master
|
||||||
|
|
||||||
|
[general]
|
||||||
|
# General section allow to set any variable that can be used by the user.
|
||||||
|
# It is also a good place to set freely global variables for personal use.
|
||||||
|
# Set some compiling values
|
||||||
|
CFLAGS="-O2 -pipe -march=native"
|
||||||
|
CXXFLAGS="$CFLAGS"
|
||||||
|
MAKEFLAGS='-j12'
|
||||||
|
PKGSOURCES='/share/src/archives'
|
||||||
|
|
||||||
|
[aliases]
|
||||||
|
# Aliases section is used to set user aliases, it is loaded only for
|
||||||
|
# interactive shells.
|
||||||
|
# Various ls aliases
|
||||||
|
ll='ls -laFh --color=auto'
|
||||||
|
la='ls -Ah --color=auto'
|
||||||
|
l='ls -CF --color=auto'
|
||||||
|
ls='ls --color=auto'
|
||||||
|
|
||||||
|
# Add color to grep output
|
||||||
|
grep='grep --color=auto'
|
||||||
|
egrep='egrep --color=auto'
|
||||||
|
fgrep='fgrep --color=auto'
|
||||||
|
|
||||||
|
# Quick find alias
|
||||||
|
qfind="find . -name "
|
||||||
|
|
||||||
|
# Some alias for compiling
|
||||||
|
mk='make'
|
||||||
|
mkck='make check'
|
||||||
|
mkin='make install'
|
||||||
|
mkdin='make DESTDIR=$PWD/dest-install install'
|
||||||
|
|
||||||
|
# ssh alias with X11 forwarding, without right restriction
|
||||||
|
ssh='ssh -Y'
|
||||||
|
|
||||||
|
# Resume mode for wget
|
||||||
|
wget='wget -c' # resume mode by default
|
||||||
|
|
||||||
|
# Human readable by default
|
||||||
|
df='df -H'
|
||||||
|
du='du -ch'
|
||||||
|
sdu='du -sk ./* | sort -n'
|
||||||
|
hdu='du -hs ./* | sort -H'
|
||||||
|
|
||||||
|
# Readable dmesg timestamps
|
||||||
|
dmesg='dmesg -T'
|
||||||
|
|
||||||
|
# End of profile.conf
|
||||||
114
doc/todo.md
Executable file
114
doc/todo.md
Executable file
@@ -0,0 +1,114 @@
|
|||||||
|
# profile — to-do list
|
||||||
|
|
||||||
|
Items marked **[easy]**, **[medium]**, or **[hard]** give a rough effort hint.
|
||||||
|
Items marked **[breaking]** would change existing behaviour and require a
|
||||||
|
version-bump.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Shell / compatibility
|
||||||
|
|
||||||
|
- [ ] **ZSH compatibility layer** — most functions work already; the remaining
|
||||||
|
blockers are `local -A` (no associative arrays in ZSH without `typeset -A`)
|
||||||
|
and `local -n` namerefs. A thin compatibility shim would open the project to
|
||||||
|
ZSH users. **[hard]**
|
||||||
|
- [ ] **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]**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Prompt & theming
|
||||||
|
|
||||||
|
- [ ] **Git branch in prompt** — show the current branch name (and dirty
|
||||||
|
indicator) in the PS1 bar when inside a Git repository. Should be
|
||||||
|
gated behind a `[prompt]` config key so it can be disabled. **[medium]**
|
||||||
|
- [ ] **Virtual-env / conda indicator** — detect `$VIRTUAL_ENV` / `$CONDA_DEFAULT_ENV`
|
||||||
|
and display the name in the prompt bar. **[easy]**
|
||||||
|
- [ ] **True-colour terminal auto-detection** — query `$COLORTERM` and
|
||||||
|
`$TERM` at load time; automatically fall back from a 24-bit theme to its
|
||||||
|
16-colour equivalent when the terminal does not support true colour. **[medium]**
|
||||||
|
- [ ] **True-colour variants of other themes** — create `monokai-tc.theme`,
|
||||||
|
`abyss-tc.theme`, etc. using the same `\e[38;2;R;G;Bm` approach as the
|
||||||
|
Solarized themes. **[easy]** _(per theme)_
|
||||||
|
- [ ] **Theme preview command** — add a `theme_preview` (or `profile_theme`)
|
||||||
|
function that renders a colour swatch and a sample prompt line for the
|
||||||
|
currently loaded theme, so users can evaluate themes without reloading
|
||||||
|
the session. **[medium]**
|
||||||
|
- [ ] **`load_theme` — DEFAULTCOL rebuild** — after overriding `DEFAULTFG` and
|
||||||
|
`DEFAULTBG`, automatically recompute `DEFAULTCOL` from the new values instead
|
||||||
|
of requiring the theme file to set it explicitly. **[easy]**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Module improvements
|
||||||
|
|
||||||
|
### compress
|
||||||
|
- [ ] **`taz` progress bar** — show a `pv` / `dd`-based progress indicator when
|
||||||
|
compressing large trees, gated behind a `-p` flag. **[medium]**
|
||||||
|
- [ ] **`utaz` integrity check** — run `tar -tOf` / `unzip -t` / `7z t` before
|
||||||
|
extracting and abort if the archive is corrupt. **[easy]**
|
||||||
|
|
||||||
|
### filefct
|
||||||
|
- [ ] **`findbig` / `findzero` / `finddead` — `fd` integration** — optionally
|
||||||
|
use `fd` instead of `find` when available for faster traversal. **[easy]**
|
||||||
|
- [ ] **`file_stats` — human-readable totals** — add `--human` flag to emit
|
||||||
|
sizes in K/M/G instead of bytes. **[easy]**
|
||||||
|
|
||||||
|
### info
|
||||||
|
- [ ] **`showinfo` fallback** — when neither `neofetch` nor `fastfetch` is
|
||||||
|
installed, print a minimal sysinfo block (hostname, OS, kernel, uptime,
|
||||||
|
CPU, RAM) using pure Bash + `/proc`. **[medium]**
|
||||||
|
|
||||||
|
### net
|
||||||
|
- [ ] **`dwl` resume support** — pass `-C -` to curl / `--continue-at -` to
|
||||||
|
wget for interrupted downloads; gate behind a `-r` flag. **[easy]**
|
||||||
|
- [ ] **`myextip` multiple providers** — fall back to a secondary URL
|
||||||
|
(configurable via `MYEXTIP_FALLBACK_URL`) when the primary times out.
|
||||||
|
**[easy]**
|
||||||
|
|
||||||
|
### processes
|
||||||
|
- [ ] **`ku` dry-run flag** — add `-n` / `--dry-run` to print what would be
|
||||||
|
killed without acting. **[easy]**
|
||||||
|
|
||||||
|
### pwd
|
||||||
|
- [ ] **`genpwd` passphrase mode** — add `-w` / `--words N` to generate
|
||||||
|
word-based passphrases (diceware-style) from `/usr/share/dict/words`.
|
||||||
|
**[medium]**
|
||||||
|
|
||||||
|
### ssh
|
||||||
|
- [ ] **SSH agent management** — add `ssh_agent_start` / `ssh_agent_stop` helpers
|
||||||
|
that start a persistent `ssh-agent`, add configured keys, and survive
|
||||||
|
re-login via a socket stored in `~/.ssh/agent.env`. **[medium]**
|
||||||
|
- [ ] **`rmhost` glob support** — allow `rmhost '*.example.com'` to remove all
|
||||||
|
matching entries in one call. **[easy]**
|
||||||
|
|
||||||
|
### updates
|
||||||
|
- [ ] **Automatic update check age** — store a timestamp in `~/.cache/profile_last_check`;
|
||||||
|
skip the network request in `check_updates -q` if the last check was less
|
||||||
|
than `UPDT_CHECK_INTERVAL` hours ago (configurable, default 24). **[medium]**
|
||||||
|
- [ ] **Changelog display** — after a successful `profile_upgrade`, fetch and
|
||||||
|
display `CHANGELOG.md` entries newer than the previously installed version.
|
||||||
|
**[medium]**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Infrastructure
|
||||||
|
|
||||||
|
- [ ] **Test suite** — add a `test/` directory with `bats` (Bash Automated
|
||||||
|
Testing System) unit tests for pure functions (`expandlist`, `genpwd`,
|
||||||
|
`pwdscore`, `isipv4`, `isipv6`, `parse_conf`, `load_theme`, …). Target for
|
||||||
|
CI. **[hard]**
|
||||||
|
- [ ] **CI pipeline** — Gitea Actions (or similar) job that runs `shellcheck`
|
||||||
|
and the `bats` suite on every push. **[medium]**
|
||||||
|
- [ ] **`profile_status` function** — print a diagnostic summary: installed
|
||||||
|
version, active theme, loaded modules, detected Bash version, interactive /
|
||||||
|
login shell flags, and `profile.conf` path. **[easy]**
|
||||||
|
- [ ] **`profile_uninstall` function** — remove the `source` line from
|
||||||
|
`~/.bashrc` / `~/.profile` and optionally delete the install directory,
|
||||||
|
with a dry-run mode. **[medium]**
|
||||||
|
- [ ] **`disp` syslog integration** — add a `DISP_SYSLOG=1` config key that
|
||||||
|
additionally pipes E/W messages to `logger`. **[easy]**
|
||||||
|
- [ ] **XDG base-dir support** — honour `$XDG_CONFIG_HOME` as an alternative
|
||||||
|
location for `profile.conf` so users can keep `~` tidy. **[medium]**
|
||||||
|
**[breaking]**
|
||||||
147
history.txt
Normal file
147
history.txt
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
------------------------------------------------------------------------------
|
||||||
|
Initial version from Beyond Linux From Scratch by
|
||||||
|
* James Robertson <jameswrobertson@earthlink.net>
|
||||||
|
* Dagmar d'Surreal <rivyqntzne@pbzpnfg.arg>
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
Current version from Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
Version history:
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
# 05/03/2026 v3.6.1
|
||||||
|
Fix a typo in compress.sh
|
||||||
|
|
||||||
|
# 05/03/2026 v3.6.0
|
||||||
|
Improved utaz to make it multiformat with lot of it
|
||||||
|
Introduced ppu and ppn
|
||||||
|
Improved update system
|
||||||
|
|
||||||
|
# 04/03/2026 v3.5.0
|
||||||
|
rain has now configurable speed and color
|
||||||
|
showinfo adapted to fastfetch, replacing neofetch
|
||||||
|
|
||||||
|
# 24/02/2022 v3.3.1
|
||||||
|
Fixed version detection
|
||||||
|
Added "busy" function
|
||||||
|
Fixed use of library functions before it's loaded
|
||||||
|
|
||||||
|
# 28/11/2022 v3.3.0
|
||||||
|
Initial version update support
|
||||||
|
Changed versioning code
|
||||||
|
Added installation path detection
|
||||||
|
|
||||||
|
# 28/11/2022 v3.2.3
|
||||||
|
Made proper readme file, to improve
|
||||||
|
|
||||||
|
# 21/11/2022 v3.2.2
|
||||||
|
Fixed taz compression level analysis
|
||||||
|
Fixed typo in dpkgs
|
||||||
|
|
||||||
|
# 20/11/2022 v3.2.1
|
||||||
|
Fix some messages
|
||||||
|
Make dpkgs rpm aware (more to come)
|
||||||
|
Removed version history from main script and revert declaration order
|
||||||
|
Added required license information in all files
|
||||||
|
Completed LICENSE file
|
||||||
|
|
||||||
|
# 18/11/2022 v3.2.0
|
||||||
|
Created disp command for display and make use of it
|
||||||
|
|
||||||
|
# 10/11/2022 v3.1.1
|
||||||
|
genpwd: test if password is doable
|
||||||
|
|
||||||
|
# 08/11/2022 v3.1.0
|
||||||
|
Added password generator
|
||||||
|
|
||||||
|
# 07/11/2022 v3.0.1
|
||||||
|
Added concatenation to rmspc
|
||||||
|
Added ku
|
||||||
|
Error managed in meteo
|
||||||
|
|
||||||
|
# 27/08/2022 v3.0.0
|
||||||
|
Splitted everything in several files
|
||||||
|
Added rain screensaver
|
||||||
|
|
||||||
|
# 29/07/2022 v2.8.2
|
||||||
|
Added warning for non bash or zsh users
|
||||||
|
|
||||||
|
# 19/07/2022 v2.8.1
|
||||||
|
Few cleanups, fixes and optimizations
|
||||||
|
|
||||||
|
# 24/06/2022 v2.8.0
|
||||||
|
Added backtrace, error and settrace
|
||||||
|
[bugfix] corrected showinfo
|
||||||
|
|
||||||
|
# 22/06/2022 v2.7.1
|
||||||
|
[bugfix] few minor corrections
|
||||||
|
Added help command
|
||||||
|
|
||||||
|
# 21/06/2022 v2.7.0
|
||||||
|
Added isipv4 and isipv6 and use it in rmhost as an improvement
|
||||||
|
Removed konsole save and restore not working
|
||||||
|
|
||||||
|
# 18/10/2021 v2.6.3
|
||||||
|
Changed PS1 for status bar style version
|
||||||
|
Few minor improvements
|
||||||
|
|
||||||
|
# 26/02/2021 v2.6.2
|
||||||
|
[bugfix] taz: corrected bug with trailing slash on directories
|
||||||
|
|
||||||
|
# 25/12/2020 v2.6.1
|
||||||
|
Add check on rmhost
|
||||||
|
Improvements rmspc
|
||||||
|
Created expendlist
|
||||||
|
|
||||||
|
# 24/10/2020 v2.6.0
|
||||||
|
Added session save and restore for Konsole
|
||||||
|
|
||||||
|
# 11/09/2020 v2.5.3
|
||||||
|
Few more aliases, improved code consistancy and typo,
|
||||||
|
Improved utaz, removed showdiskmap, removed remaining French,
|
||||||
|
Added license information for future publication
|
||||||
|
|
||||||
|
# 06/03/2020 v2.5.2
|
||||||
|
Few aliases sorted out
|
||||||
|
|
||||||
|
# 05/03/2020 v2.5.1
|
||||||
|
Language consistancy fix
|
||||||
|
Added pigz support in taz
|
||||||
|
|
||||||
|
# 03/03/2020 v2.5.0
|
||||||
|
Added command taz and rmspc
|
||||||
|
Renamed auzip => utaz and improved it
|
||||||
|
|
||||||
|
# 02/03/2020 v2.4.0
|
||||||
|
Added command auzip
|
||||||
|
|
||||||
|
# 31/01/2020 v2.3.2
|
||||||
|
Figlet: changed default font to ansi_shadow
|
||||||
|
|
||||||
|
# 16/01/2020 v2.3.1
|
||||||
|
[bugfix] non-interactive were blocked with some functions
|
||||||
|
|
||||||
|
# 08/01/2020 v2.3.0
|
||||||
|
Added use of figlet and neofetch as a motd replace
|
||||||
|
|
||||||
|
# 16/12/2019 v2.2.0
|
||||||
|
Added showinfo
|
||||||
|
Primary write of showdiskmap
|
||||||
|
|
||||||
|
# 24/09/2019 v2.1.2
|
||||||
|
[bugfix] bug in profile version display
|
||||||
|
|
||||||
|
# 23/09/2019 v2.1.1
|
||||||
|
[bugfix] dpkgs
|
||||||
|
|
||||||
|
# 16/09/2018 v2.1.0
|
||||||
|
Added rmhost, setc, setfr
|
||||||
|
More locales management
|
||||||
|
|
||||||
|
# 04/02/2017 v2.0.1
|
||||||
|
clean improvements (--shell)
|
||||||
|
|
||||||
|
# 24/10/2015 v2.0.0
|
||||||
|
Added advanced functionnalities (clean, srr, etc.)
|
||||||
|
|
||||||
|
# 16/02/2013 v1.0.0
|
||||||
|
Initial version
|
||||||
|
|
||||||
@@ -1,108 +1,413 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Smartly uncompress archives (zip only)
|
# Copyright (c) 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
|
# Protected by the BSD3 license. Please read bellow for details.
|
||||||
|
#
|
||||||
|
# * Redistribution and use in source and binary forms,
|
||||||
|
# * with or without modification, are permitted provided
|
||||||
|
# * that the following conditions are met:
|
||||||
|
# *
|
||||||
|
# * Redistributions of source code must retain the above
|
||||||
|
# * copyright notice, this list of conditions and the
|
||||||
|
# * following disclaimer.
|
||||||
|
# *
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# * copyright notice, this list of conditions and the following
|
||||||
|
# * disclaimer in the documentation and/or other materials
|
||||||
|
# * provided with the distribution.
|
||||||
|
# *
|
||||||
|
# * Neither the name of the copyright holder nor the names
|
||||||
|
# * of any other contributors may be used to endorse or
|
||||||
|
# * promote products derived from this software without
|
||||||
|
# * specific prior written permission.
|
||||||
|
# *
|
||||||
|
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
# * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
# * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
# * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# * OF SUCH DAMAGE.
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Smartly uncompress archives
|
||||||
|
# Usage: utaz [option] [directorie(s)|file(s)]
|
||||||
|
# Options:
|
||||||
|
# -h, --help Display that help screen
|
||||||
|
# -d, --delete If decompression succeeded, delete the source file
|
||||||
|
# -c, --create-dir Always create a host directory
|
||||||
|
# -n, --no-dir Never create a host directory
|
||||||
utaz()
|
utaz()
|
||||||
{
|
{
|
||||||
for opt in $@ ; do
|
# shellcheck disable=SC2329
|
||||||
case $opt in
|
_ununzip()
|
||||||
"-h"|"--help")
|
{
|
||||||
echo "utaz: uncompress all the given files and/or the ones found in the given"
|
unzip -o "$1" -d "$2" >/dev/null 2>&1
|
||||||
echo " directories creating an host directory where needed."
|
}
|
||||||
echo
|
|
||||||
echo "Usage: utaz [option] [directorie(s)|file(s)]"
|
# shellcheck disable=SC2329
|
||||||
echo
|
_untar()
|
||||||
echo "Options:"
|
{
|
||||||
echo " -h, --help Display that help screen"
|
tar -xf "$1" -C "$2"
|
||||||
echo " -d, --delete If decompression succeeded, delete the source file"
|
}
|
||||||
echo " -c, --create-dir Always create a host directory"
|
|
||||||
echo " -n, --no-dir Never create a host directory"
|
# shellcheck disable=SC2329
|
||||||
echo
|
_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
|
||||||
|
plzip -d -c "$1" > "$2/$(basename "${1%.*}")"
|
||||||
|
else
|
||||||
|
lzip -d -c "$1" > "$2/$(basename "${1%.*}")"
|
||||||
|
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
|
||||||
|
# We ensure it hits the target directory
|
||||||
|
(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
|
||||||
|
# Needs rpm2cpio and cpio
|
||||||
|
rpm2cpio "$1" | (cd "$2/" && cpio -idmv) >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "utaz: uncompress all the given files and/or the ones found in the given\n"
|
||||||
|
printf " directories creating an host directory where needed.\n\n"
|
||||||
|
printf "Usage: utaz [option] [directorie(s)|file(s)]\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-h, --help\t\tDisplay that help screen\n"
|
||||||
|
printf "\t-d, --delete\t\tIf decompression succeeded, delete the source file\n"
|
||||||
|
printf "\t-c, --create-dir\tAlways create a host directory\n"
|
||||||
|
printf "\t-n, --no-dir\t\tNever create a host directory\n\n"
|
||||||
|
printf "Supported archive format:\n"
|
||||||
|
printf "\t- zip\n"
|
||||||
|
printf "\t- tar.gz, .tgz\n"
|
||||||
|
printf "\t- tar.bz2, .tbz2\n"
|
||||||
|
printf "\t- tar.xz, .txz\n"
|
||||||
|
printf "\t- tar.lz, .tlz\n"
|
||||||
|
printf "\t- rar\n"
|
||||||
|
printf "\t- arj\n"
|
||||||
|
printf "\t- lha, lzh\n"
|
||||||
|
printf "\t- ace\n"
|
||||||
|
printf "\t- 7z, p7z\n"
|
||||||
|
printf "\t- zst\n"
|
||||||
|
printf "\t- cpio\n"
|
||||||
|
printf "\t- cab\n"
|
||||||
|
printf "\t- deb\n"
|
||||||
|
printf "\t- rpm\n"
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
-d|--delete)
|
||||||
"-d"|"--delete")
|
|
||||||
local willrm=1
|
local willrm=1
|
||||||
|
shift
|
||||||
;;
|
;;
|
||||||
|
-c|--create-dir)
|
||||||
"-c"|"--create-dir")
|
|
||||||
local createdir=1
|
local createdir=1
|
||||||
|
shift
|
||||||
;;
|
;;
|
||||||
|
-n|--no-dir)
|
||||||
"-n"|"--no-dir")
|
|
||||||
local nodir=1
|
local nodir=1
|
||||||
|
shift
|
||||||
;;
|
;;
|
||||||
|
--)
|
||||||
"-"*)
|
shift
|
||||||
disp E "Invalid option, use \"utaz --help\" to display options list"
|
break
|
||||||
echo
|
|
||||||
return 1
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
# The ${opt%/} writing is to remove trailing / if any
|
disp E "Invalid option, use \"utaz --help\" to display options list"
|
||||||
local LIST="$LIST ${opt%/}"
|
return 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
# The remaining arguments after -- are the files/directories to process,
|
||||||
|
# "." is used if none is given
|
||||||
|
local FILES=("$@")
|
||||||
|
[[ ${#FILES[@]} -eq 0 ]] && FILES=(".")
|
||||||
|
|
||||||
[[ $createdir && $nodir ]] && disp E "The --create-dir and --no-dir options are mutually exclusive."
|
# Apply defaults from [compress] configuration if not overridden by flags
|
||||||
|
[[ -z ${willrm+x} ]] && [[ ${UTAZ_DEFAULT_DELETE:-0} -eq 1 ]] && willrm=1
|
||||||
|
if [[ -z ${createdir+x} && -z ${nodir+x} ]]; then
|
||||||
|
case "${UTAZ_DEFAULT_DIR_MODE:-auto}" in
|
||||||
|
always) createdir=1 ;;
|
||||||
|
never) nodir=1 ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
[[ ! $LIST ]] && local LIST="."
|
[[ -n ${createdir} && -n ${nodir} ]] && \
|
||||||
|
disp E "The --create-dir and --no-dir options are mutually exclusive."
|
||||||
|
|
||||||
for zitem in $LIST; do
|
for zitem in "${FILES[@]}"; do
|
||||||
[[ $(ls $zitem/*.zip 2> /dev/null | wc -l) -eq 0 ]] &&
|
# Build list of input files to process, with whitespace-safe handling.
|
||||||
disp W "$zitem contains no supported archive file, skipping." &&
|
local targets=()
|
||||||
|
if [[ -f "$zitem" ]]; then
|
||||||
|
targets=("$zitem")
|
||||||
|
elif [[ -d "$zitem" ]]; then
|
||||||
|
mapfile -d '' -t targets < <(find "$zitem" -mindepth 1 -maxdepth 1 -print0 2>/dev/null)
|
||||||
|
if [[ ${#targets[@]} -eq 0 ]]; then
|
||||||
|
disp I "Directory ${zitem} is empty, skipping."
|
||||||
continue
|
continue
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
disp W "Path ${zitem} is not a file or directory, skipping."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
for f in $zitem/*.zip; do
|
for f in "${targets[@]}"; do
|
||||||
disp I "Processing archive $zitem/$f... "
|
local dir="${f%.*}"
|
||||||
local dir=${f::-4}
|
local extractor=""
|
||||||
|
case "$f" in
|
||||||
mkdir -p $dir
|
*.zip)
|
||||||
[[ $? -gt 0 ]] &&
|
extractor="_ununzip"
|
||||||
disp E "The filesystem can't create directories, exit!" &&
|
|
||||||
return 1
|
|
||||||
|
|
||||||
unzip -o $f -d $dir > /dev/null 2>&1
|
|
||||||
case $? in
|
|
||||||
0)
|
|
||||||
[[ $willrm ]] && rm -f $f && disp I "File $zitem/$f deleted."
|
|
||||||
;;
|
;;
|
||||||
|
*.tar.gz|*.tgz)
|
||||||
1)
|
extractor="_ungzip"
|
||||||
disp W "Compression program returned a warning: deletion canceled."
|
;;
|
||||||
|
*.tar.bz2|*.tbz2)
|
||||||
|
extractor="_unbzip2"
|
||||||
|
;;
|
||||||
|
*.tar.xz|*.txz)
|
||||||
|
extractor="_unxz"
|
||||||
|
;;
|
||||||
|
*.tar.lz|*.tlz)
|
||||||
|
extractor="_unlzop"
|
||||||
|
;;
|
||||||
|
*.tar)
|
||||||
|
extractor="_untar"
|
||||||
|
;;
|
||||||
|
*.rar)
|
||||||
|
extractor="_ununrar"
|
||||||
|
;;
|
||||||
|
*.arj)
|
||||||
|
extractor="_ununarj"
|
||||||
|
;;
|
||||||
|
*.lzh|*.lha)
|
||||||
|
extractor="_unlha"
|
||||||
|
;;
|
||||||
|
*.ace)
|
||||||
|
extractor="_ununace"
|
||||||
|
;;
|
||||||
|
*.7z|*.p7z)
|
||||||
|
extractor="_un7z"
|
||||||
|
;;
|
||||||
|
*.zst)
|
||||||
|
extractor="_unzstd"
|
||||||
|
;;
|
||||||
|
*.cpio)
|
||||||
|
extractor="_uncpio"
|
||||||
|
;;
|
||||||
|
*.cab)
|
||||||
|
extractor="_uncabextract"
|
||||||
|
;;
|
||||||
|
*.deb)
|
||||||
|
extractor="_undeb"
|
||||||
|
;;
|
||||||
|
*.rpm)
|
||||||
|
extractor="_unrpm"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
disp E "The zip file seems corrupted, failed."
|
disp I "File ${f} is not a supported archive, skipping."
|
||||||
rm -rf $dir > /dev/null 2>&1
|
continue
|
||||||
|
;; # Skip non-archive files
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Verify binary existence
|
||||||
|
local cmd=${extractor//_un/}
|
||||||
|
if [[ $cmd == "deb" ]]; then
|
||||||
|
command -v -- dpkg-deb >/dev/null 2>&1 || {
|
||||||
|
disp E "The program 'dpkg-deb' is not installed, aborting."
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
elif [[ $cmd == "rpm" ]]; then
|
||||||
|
command -v -- rpm2cpio >/dev/null 2>&1 || {
|
||||||
|
disp E "The program 'rpm2cpio' is not installed, aborting."
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
command -v -- cpio >/dev/null 2>&1 || {
|
||||||
|
disp E "The program 'cpio' is not installed, aborting."
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
else
|
||||||
|
command -v -- "${cmd}" >/dev/null 2>&1 || {
|
||||||
|
disp E "Binary ${cmd} necessary to extract ${f} is missing."
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
disp I "Processing archive ${f} with ${extractor}..."
|
||||||
|
if ! mkdir -p "${dir}"; then
|
||||||
|
disp E "The filesystem can't create directories, exit!" &&
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
${extractor} "${f}" "${dir}"
|
||||||
|
case $? in
|
||||||
|
0)
|
||||||
|
[[ -n ${willrm} ]] &&
|
||||||
|
rm -f "${f}" && disp I "File ${zitem}/${f} deleted."
|
||||||
|
;;
|
||||||
|
1)
|
||||||
|
if [[ -n ${willrm} ]]; then
|
||||||
|
disp W "Compression program returned a warning: deletion canceled."
|
||||||
|
else
|
||||||
|
disp W "Compression program returned a warning."
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "The compressed file ${f} seems corrupted, failed."
|
||||||
|
rm -rf "${dir}" >/dev/null 2>&1
|
||||||
continue
|
continue
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
if [[ $createdir ]]; then
|
if [[ -n ${createdir} ]]; then
|
||||||
disp I "Archive extracted successfully in subdirectory."
|
disp I "Archive extracted successfully in subdirectory."
|
||||||
elif [[ $nodir ]]; then
|
elif [[ -n ${nodir} ]]; then
|
||||||
mv ./$dir/* ./ && rmdir $dir
|
shopt -s nullglob
|
||||||
|
for child in "${dir}"/*; do
|
||||||
|
mv -- "$child" .
|
||||||
|
done
|
||||||
|
shopt -u nullglob
|
||||||
|
rmdir -- "${dir}"
|
||||||
disp I "Archive extracted successfully, no subdirectory needed."
|
disp I "Archive extracted successfully, no subdirectory needed."
|
||||||
else
|
else
|
||||||
subdirs=$(find $dir -maxdepth 1 | wc -l)
|
# Set nullglob to ensure the array is empty if no files match
|
||||||
if [[ $subdirs -eq 2 ]]; then
|
shopt -s nullglob
|
||||||
mv ./$dir/* ./ && rmdir $dir
|
local contents=( "${dir}"/* )
|
||||||
|
|
||||||
|
# Check if exactly one item exists and if that item is a directory
|
||||||
|
if [[ ${#contents[@]} -eq 1 ]] && [[ -d "${contents[0]}" ]]; then
|
||||||
|
# Single directory detected
|
||||||
|
shopt -s nullglob
|
||||||
|
for child in "${contents[0]}"/*; do
|
||||||
|
mv -- "$child" .
|
||||||
|
done
|
||||||
|
shopt -u nullglob
|
||||||
|
rmdir -- "${dir}"
|
||||||
disp I "Archive extracted successfully, no subdirectory needed."
|
disp I "Archive extracted successfully, no subdirectory needed."
|
||||||
else
|
else
|
||||||
disp I "Archive extracted successfully in subdirectory."
|
disp I "Archive extracted successfully in subdirectory."
|
||||||
fi
|
fi
|
||||||
|
shopt -u nullglob
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
export -f utaz
|
export -f utaz
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Compress directories or files into one or more archive
|
# Compress directories or files into one or more archive
|
||||||
# ------------------------------------------------------------------------------
|
# Usage: taz [option] [--parallel=<n>] [--format=<format>] [directory1 ... directoryN]
|
||||||
taz ()
|
# Options:
|
||||||
|
# -h, --help Display that help screen
|
||||||
|
# -d, --delete Delete source file or directory after success
|
||||||
|
# -f, --format Chose archive format in the given list. If several format are
|
||||||
|
# given, the smalest is kept
|
||||||
|
# -p, --parallel Number of threads to use (if allowed by underlying utility)
|
||||||
|
# -v, --verbose Display progress where possible
|
||||||
|
# -q, --quiet Display less messages (only errors and warnings)
|
||||||
|
# -1, .., -9 Compression level to use [1=fast/biggest, 9=slow/smallest]
|
||||||
|
taz()
|
||||||
{
|
{
|
||||||
|
# shellcheck disable=SC2329
|
||||||
_doxz()
|
_doxz()
|
||||||
{
|
{
|
||||||
command -v xz >/dev/null 2>&1 || {
|
command -v xz >/dev/null 2>&1 || {
|
||||||
@@ -110,17 +415,19 @@ taz ()
|
|||||||
return 127
|
return 127
|
||||||
}
|
}
|
||||||
|
|
||||||
[[ $4 ]] && local verb='-v'
|
local verb=()
|
||||||
|
[[ $4 ]] && verb=('-v')
|
||||||
|
|
||||||
# Display a warning for this format
|
# Display a warning for this format
|
||||||
disp W "xz format is not suited for long term archiving."
|
disp W "xz format is not suited for long term archiving."
|
||||||
disp I "See https://www.nongnu.org/lzip/xz_inadequate.html for details."
|
disp I "See https://www.nongnu.org/lzip/xz_inadequate.html for details."
|
||||||
|
|
||||||
# Compresse to xz (lzma2) - Deprecated
|
# Compress with xz (lzma2) - Deprecated
|
||||||
xz $verb --compress --keep -$3 -T $2 $1
|
xz "${verb[@]}" --compress --keep "-$3" -T "$2" "$1"
|
||||||
return $?
|
return $?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# shellcheck disable=SC2329
|
||||||
_dolz()
|
_dolz()
|
||||||
{
|
{
|
||||||
local procopt="--threads $2"
|
local procopt="--threads $2"
|
||||||
@@ -131,20 +438,23 @@ taz ()
|
|||||||
disp E "Program 'plzip' or 'lzip' are not installed, aborting."
|
disp E "Program 'plzip' or 'lzip' are not installed, aborting."
|
||||||
return 127
|
return 127
|
||||||
}
|
}
|
||||||
local command=lzip
|
command=lzip
|
||||||
local procopt=""
|
local procopt=""
|
||||||
[[ $2 -gt 1 ]] &&
|
[[ $2 -gt 1 ]] &&
|
||||||
disp W "lzip doesn't support multithreading, falling back to 1 thread." &&
|
disp W "lzip doesn't support multithreading, falling back to 1 thread." &&
|
||||||
disp W "Consitder installing plzip to obtain multithreading abilities."
|
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)
|
# Compress with lzip (lzma)
|
||||||
$command $verb $procopt --keep -$3 $1
|
$command "${opt[@]}" --keep "-$3" "$1"
|
||||||
return $?
|
return $?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# shellcheck disable=SC2329
|
||||||
_dogz()
|
_dogz()
|
||||||
{
|
{
|
||||||
local procopt="--processes $2"
|
local procopt="--processes $2"
|
||||||
@@ -159,16 +469,19 @@ taz ()
|
|||||||
local procopt=""
|
local procopt=""
|
||||||
[[ $2 -gt 1 ]] &&
|
[[ $2 -gt 1 ]] &&
|
||||||
disp W "gzip doesn't support multithreading, falling back to 1 thread." &&
|
disp W "gzip doesn't support multithreading, falling back to 1 thread." &&
|
||||||
disp W "Consitder installing pigz to obtain multithreading abilities."
|
disp W "Consider installing pigz to obtain multithreading abilities."
|
||||||
}
|
}
|
||||||
|
|
||||||
[[ $4 ]] && local verb="--verbose"
|
local opt=()
|
||||||
|
[[ $4 ]] && opt=('--verbose')
|
||||||
|
opt+=("$procopt")
|
||||||
|
|
||||||
# Compresse au format bz2
|
# Compresse au format bz2
|
||||||
$command $verb $procopt --keep -$3 $1
|
$command "${opt[@]}" --keep "-$3" "$1"
|
||||||
return $?
|
return $?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# shellcheck disable=SC2329
|
||||||
_dobz2()
|
_dobz2()
|
||||||
{
|
{
|
||||||
local procopt="-p$2"
|
local procopt="-p$2"
|
||||||
@@ -183,16 +496,19 @@ taz ()
|
|||||||
local procopt=""
|
local procopt=""
|
||||||
[[ $2 -gt 1 ]] &&
|
[[ $2 -gt 1 ]] &&
|
||||||
disp W "bzip2 doesn't support multithreading, falling back to 1 thread." &&
|
disp W "bzip2 doesn't support multithreading, falling back to 1 thread." &&
|
||||||
disp W "Consitder installing pbzip2 to obtain multithreading abilities."
|
disp W "Consider installing pbzip2 to obtain multithreading abilities."
|
||||||
}
|
}
|
||||||
|
|
||||||
[[ $4 ]] && local verb="-v"
|
local opt=()
|
||||||
|
[[ $4 ]] && opt=('-v')
|
||||||
|
opt+=("$procopt")
|
||||||
|
|
||||||
# Compresse au format bz2
|
# Compress with bz2
|
||||||
$command $verb --compress $procopt --keep -$3 $1
|
$command "${opt[@]}" --compress --keep "-$3" "$1"
|
||||||
return $?
|
return $?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# shellcheck disable=SC2329
|
||||||
_dolzo()
|
_dolzo()
|
||||||
{
|
{
|
||||||
command -v lzop >/dev/null 2>&1 || {
|
command -v lzop >/dev/null 2>&1 || {
|
||||||
@@ -200,94 +516,109 @@ taz ()
|
|||||||
return 127
|
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."
|
[[ $2 -gt 1 ]] && disp W "lzop doesn't support multithreading, falling back to 1 thread."
|
||||||
|
|
||||||
# Compresse au format lzo
|
# Compresse au format lzo
|
||||||
lzop --keep -$3 $1
|
lzop "${verb[@]}" --keep "-$3" "$1"
|
||||||
return $?
|
return $?
|
||||||
}
|
}
|
||||||
|
|
||||||
for opt in $@ ; do
|
local PARSED
|
||||||
case $opt in
|
PARSED=$(getopt -o hdf:p:vq123456789 --long help,delete,format:,parallel:,verbose,quiet --name "taz" -- "$@")
|
||||||
"-h"|"--help")
|
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||||
echo "taz: archive all files of a directory."
|
if [ $? -ne 0 ]; then
|
||||||
echo
|
disp E "Invalid options, use \"taz --help\" to display usage."
|
||||||
echo "Usage: taz [option] [--parallel=<n>] [--format=<format>] [directory1 ... directoryN]"
|
return 1
|
||||||
echo
|
fi
|
||||||
echo "Options:"
|
eval set -- "$PARSED"
|
||||||
echo " -h, --help Display that help screen"
|
while true; do
|
||||||
echo " -d, --delete Delete source file or directory after success"
|
case "$1" in
|
||||||
echo " -f, --format Chose archive format in the given list. If several format are"
|
-h|--help)
|
||||||
echo " given, the smalest is kept"
|
printf "taz: archive all files of a directory.\n\n"
|
||||||
echo " -p, --parallel Number of threads to use (if allowed by underlying utility)"
|
printf "Usage: taz [option] [--parallel=<n>] [--format=<format>] [directory1 ... directoryN]\n\n"
|
||||||
echo " -v, --verbose Display progress where possible"
|
printf "Options:\n"
|
||||||
echo " -q, --quiet Display less messages (only errors and warnings)"
|
printf "\t-h, --help\tDisplay that help screen\n"
|
||||||
echo " -1, .., -9 Compression level to use [1=fast/big, 9=slow/small]"
|
printf "\t-d, --delete\tDelete source file or directory after success\n"
|
||||||
echo
|
printf "\t-f, --format\tChose archive format in the given list. If several format are"
|
||||||
echo "Supported archive format:"
|
printf "\t\t\tgiven, the smalest is kept\n"
|
||||||
echo " Param.| programs | Algo. | Description"
|
printf "\t-p, --parallel\tNumber of threads to use (if allowed by underlying utility)\n"
|
||||||
echo " ------+---------------+-------+----------------------------------------"
|
printf "\t-v, --verbose\tDisplay progress where possible\n"
|
||||||
echo " lz | plzip, lzip | lzma | Safe efficient default format"
|
printf "\t-q, --quiet\tDisplay less messages (only errors and warnings)\n"
|
||||||
echo " xz | xz | lzma2 | Unsafe, not for long term"
|
printf "\t-1, .., -9\tCompression level to use [1=fast/biggest, 9=slow/smallest]\n\n"
|
||||||
echo " bz2 | pbzip2, bzip2 | bzip2 | Historical but less efficient than lz"
|
printf "Supported archive format:\n"
|
||||||
echo " gz | pigz, gzip | lz77 | Historical, safe, fast"
|
printf "\tParam.| programs | Algo. | Description\n"
|
||||||
echo " lzo | lzop | lzo | Very fast but no multithread"
|
printf "\t------+---------------+-------+----------------------------------------\n"
|
||||||
echo " tar | tar | tar | No compression"
|
printf "\t lz | plzip, lzip | lzma | Safe efficient default format\n"
|
||||||
echo
|
printf "\t xz | xz | lzma2 | Unsafe, not for long term\n"
|
||||||
|
printf "\t bz2 | pbzip2, bzip2 | bzip2 | Historical but less efficient than lz\n"
|
||||||
|
printf "\t gz | pigz, gzip | lz77 | Historical, safe, fast\n"
|
||||||
|
printf "\t lzo | lzop | lzo | Very fast but no multithread\n"
|
||||||
|
printf "\t tar | tar | tar | No compression\n"
|
||||||
|
printf "\n"
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
|
||||||
"-d"|"--delete")
|
-d|--delete)
|
||||||
local willrm=1
|
local willrm=1
|
||||||
|
shift
|
||||||
;;
|
;;
|
||||||
|
|
||||||
"-f"?*|"--format"?*)
|
-f|--format)
|
||||||
local compform=$(echo "$opt" | cut -f 2- -d '=')
|
local compform=$2
|
||||||
|
shift 2
|
||||||
;;
|
;;
|
||||||
|
|
||||||
"-p"?*|"--parallel"?*)
|
-p|--parallel)
|
||||||
local nproc=$(echo "$opt" | cut -f 2- -d '=')
|
local nproc=$2
|
||||||
|
shift 2
|
||||||
;;
|
;;
|
||||||
|
|
||||||
"-v"|"--verbose")
|
-v|--verbose)
|
||||||
local verbose=1
|
local verbose=1
|
||||||
|
shift
|
||||||
;;
|
;;
|
||||||
|
|
||||||
"-q"|"--quiet")
|
-q|--quiet)
|
||||||
QUIET=1
|
local quiet=1
|
||||||
|
shift
|
||||||
"-"[1..9])
|
|
||||||
local complevel=$(echo $opt | sed 's/-//')
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
"-"*)
|
-[1-9])
|
||||||
echo "Invalid option, use taz --help to display options list"
|
complevel="${1#-}"
|
||||||
echo
|
shift
|
||||||
return 1
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
local LIST="$LIST ${opt%/}"
|
disp E "Invalid option, use \"taz --help\" to display options list"
|
||||||
|
return 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
[[ ! $compform ]] && compform=lz # safe and efficient (unless data are already compressed)
|
# The remaining arguments after -- are the files/directories to process,
|
||||||
[[ ! $nproc ]] && nproc=1
|
# "." is used if none is given
|
||||||
[[ ! $complevel ]] && complevel=6
|
local FILES=("$@")
|
||||||
[[ $verbose -gt 1 && $QUIET -gt 1 ]] &&
|
[[ ${#FILES[@]} -eq 0 ]] && FILES=(".")
|
||||||
|
|
||||||
|
[[ ! $compform ]] && compform=${TAZ_DEFAULT_FORMAT:-lz}
|
||||||
|
[[ ! $nproc ]] && nproc=${TAZ_DEFAULT_THREADS:-1}
|
||||||
|
[[ ! $complevel ]] && complevel=${TAZ_DEFAULT_LEVEL:-6}
|
||||||
|
[[ $verbose -gt 1 && $quiet -gt 1 ]] &&
|
||||||
disp E "The --verbose and --quiet options can't be used together."
|
disp E "The --verbose and --quiet options can't be used together."
|
||||||
|
|
||||||
for item in $LIST; do
|
for item in "${FILES[@]}"; do
|
||||||
local donetar=0
|
local donetar=0
|
||||||
disp I "Processing $item..."
|
disp I "Processing $item..."
|
||||||
|
|
||||||
if [[ -d $item ]]; then
|
if [[ -d "$item" ]]; then
|
||||||
disp I "\t Creating $item.tar... "
|
disp I "\t Creating $item.tar... "
|
||||||
|
|
||||||
tar -cf $item{.tar,}
|
if ! tar -cf "$item.tar" "$item"; then
|
||||||
if [[ ! $? -eq 0 ]]; then
|
|
||||||
disp E "tar file creation failed, skipping to next item."
|
disp E "tar file creation failed, skipping to next item."
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
@@ -301,8 +632,9 @@ taz ()
|
|||||||
# Skip compression part if tar is asked
|
# Skip compression part if tar is asked
|
||||||
if [[ $compform != "tar" ]]; then
|
if [[ $compform != "tar" ]]; then
|
||||||
disp I "\t Compressing archive..."
|
disp I "\t Compressing archive..."
|
||||||
_do$compform $fname $nproc $complevel $verbose
|
local exec_code=0
|
||||||
[[ ! $? -eq 0 ]] && case $? in
|
"_do$compform" "$fname" "$nproc" "$complevel" "$verbose" || exec_code=$?
|
||||||
|
[[ ! $exec_code -eq 0 ]] && case $exec_code in
|
||||||
127)
|
127)
|
||||||
disp E "Compression program unavailable, aborting."
|
disp E "Compression program unavailable, aborting."
|
||||||
return 127
|
return 127
|
||||||
@@ -313,14 +645,20 @@ taz ()
|
|||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
[[ $donetar -gt 0 ]] && rm $fname
|
[[ $donetar -gt 0 ]] && rm "$fname"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ $willrm ]]; then
|
if [[ $willrm ]]; then
|
||||||
disp I "\t Deleting original source as asked... "
|
disp I "\t Deleting original source as asked... "
|
||||||
rm -r $item
|
rm -r "$item"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
unset QUIET
|
unset quiet
|
||||||
}
|
}
|
||||||
export -f taz
|
export -f taz
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
load_conf "compress"
|
||||||
|
|
||||||
|
# EOF
|
||||||
|
|||||||
@@ -1,72 +1,131 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright (c) 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
|
# Protected by the BSD3 license. Please read bellow for details.
|
||||||
|
#
|
||||||
|
# * Redistribution and use in source and binary forms,
|
||||||
|
# * with or without modification, are permitted provided
|
||||||
|
# * that the following conditions are met:
|
||||||
|
# *
|
||||||
|
# * Redistributions of source code must retain the above
|
||||||
|
# * copyright notice, this list of conditions and the
|
||||||
|
# * following disclaimer.
|
||||||
|
# *
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# * copyright notice, this list of conditions and the following
|
||||||
|
# * disclaimer in the documentation and/or other materials
|
||||||
|
# * provided with the distribution.
|
||||||
|
# *
|
||||||
|
# * Neither the name of the copyright holder nor the names
|
||||||
|
# * of any other contributors may be used to endorse or
|
||||||
|
# * promote products derived from this software without
|
||||||
|
# * specific prior written permission.
|
||||||
|
# *
|
||||||
|
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
# * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
# * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
# * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# * OF SUCH DAMAGE.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Display a backtrace
|
# Display a backtrace
|
||||||
# ------------------------------------------------------------------------------
|
# Usage: backtrace
|
||||||
function backtrace ()
|
function backtrace()
|
||||||
{
|
{
|
||||||
echo "========= Call stack ========="
|
printf "========= Call stack =========\n"
|
||||||
typeset -i i=0
|
local i=1 # We begin at 1 to ignore backtrace itself
|
||||||
|
while [[ $i -lt ${#FUNCNAME[@]} ]]; do
|
||||||
local func=
|
|
||||||
for func in "${FUNCNAME[@]}"; do
|
|
||||||
if [[ $i -ne 0 ]]; then
|
|
||||||
printf '%15s() %s:%d\n' \
|
printf '%15s() %s:%d\n' \
|
||||||
"$func" "${BASH_SOURCE[$i]}" "${BASH_LINENO[ (( $i - 1)) ]}"
|
"${FUNCNAME[$i]}" "${BASH_SOURCE[$i]}" "${BASH_LINENO[$(( i-1 ))]}"
|
||||||
fi
|
((i++))
|
||||||
let i++ || true
|
|
||||||
done
|
done
|
||||||
unset func i
|
unset i
|
||||||
echo "=============================="
|
printf "==============================\n"
|
||||||
}
|
}
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Function to be trapped for errors investigation
|
# Function to be trapped for errors investigation
|
||||||
# ------------------------------------------------------------------------------
|
function error()
|
||||||
function error ()
|
|
||||||
{
|
{
|
||||||
local errcode=$?
|
local errcode=$?
|
||||||
backtrace
|
backtrace
|
||||||
return $errcode
|
return $errcode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Activate or deactivate error trapping to display backtrace
|
# Activate or deactivate error trapping to display backtrace
|
||||||
# ------------------------------------------------------------------------------
|
# Usage: settrace <--on|--off|--status>
|
||||||
settrace ()
|
settrace()
|
||||||
{
|
{
|
||||||
local status="off"
|
local status="off"
|
||||||
[[ $(trap -p ERR) ]] && status="on"
|
[[ $(trap -p ERR) ]] && status="on"
|
||||||
#trap -p ERR
|
#trap -p ERR
|
||||||
for opt in $@ ; do
|
|
||||||
case $opt in
|
local PARSED
|
||||||
"-h"|"--help")
|
PARSED=$(getopt -oh --long help,on,off,status,force -- "$@")
|
||||||
echo "Try to activate backtrace display for script debugging."
|
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||||
echo
|
if [[ $? -ne 0 ]]; then
|
||||||
echo "Options:"
|
disp E "Invalid options, use \"settrace --help\" to display usage."
|
||||||
echo " --on Activate backtrace generation"
|
return 1
|
||||||
echo " --off Deactivate backtrace generation"
|
fi
|
||||||
echo
|
eval set -- "$PARSED"
|
||||||
echo "That function active a trap event on error. If the script you want to"
|
local force=0
|
||||||
echo "debug overload the ERR bash trap, it will not work."
|
while true; do
|
||||||
echo
|
case $1 in
|
||||||
|
-h|--help)
|
||||||
|
printf "Try to activate backtrace display for script debugging.\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t--on\t\tActivate backtrace generation\n"
|
||||||
|
printf "\t--force\t\tForce replacement of existing trap (use with --on)\n"
|
||||||
|
printf "\t--off\t\tDeactivate backtrace generation\n\n"
|
||||||
|
printf "That function active a trap event on error. If the script you want to\n"
|
||||||
|
printf "debug overload the ERR bash trap, it will not work.\n"
|
||||||
|
return 0
|
||||||
;;
|
;;
|
||||||
"--on")
|
--on)
|
||||||
if [[ $status == "on" ]]; then
|
if [[ ${status} == "on" ]] && [[ $force -eq 0 ]]; then
|
||||||
disp W "ERR signal trap is already set, replacing previous trap!"
|
disp E "ERR signal trap is already set. Use --force to replace it."
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
trap "error" ERR
|
trap "error" ERR
|
||||||
|
shift
|
||||||
;;
|
;;
|
||||||
"--off")
|
--force)
|
||||||
if [[ $status != "on" ]]; then
|
force=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--off)
|
||||||
|
if [[ ${status} != "on" ]]; then
|
||||||
disp W "ERR signal trap is already unset!"
|
disp W "ERR signal trap is already unset!"
|
||||||
fi
|
fi
|
||||||
trap - ERR
|
trap - ERR
|
||||||
|
shift
|
||||||
;;
|
;;
|
||||||
"--status")
|
--status)
|
||||||
disp "ERR trap signal is ${status}."
|
disp I "Trap signal is ${status}."
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid options, use \"settrace --help\" to display usage."
|
||||||
|
return 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
unset status
|
unset status force
|
||||||
}
|
}
|
||||||
export -f settrace
|
export -f settrace
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# EOF
|
||||||
|
|||||||
@@ -1,112 +1,188 @@
|
|||||||
# Color definitions
|
#!/usr/bin/env bash
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright (c) 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
|
# Protected by the BSD3 license. Please read bellow for details.
|
||||||
|
#
|
||||||
|
# * Redistribution and use in source and binary forms,
|
||||||
|
# * with or without modification, are permitted provided
|
||||||
|
# * that the following conditions are met:
|
||||||
|
# *
|
||||||
|
# * Redistributions of source code must retain the above
|
||||||
|
# * copyright notice, this list of conditions and the
|
||||||
|
# * following disclaimer.
|
||||||
|
# *
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# * copyright notice, this list of conditions and the following
|
||||||
|
# * disclaimer in the documentation and/or other materials
|
||||||
|
# * provided with the distribution.
|
||||||
|
# *
|
||||||
|
# * Neither the name of the copyright holder nor the names
|
||||||
|
# * of any other contributors may be used to endorse or
|
||||||
|
# * promote products derived from this software without
|
||||||
|
# * specific prior written permission.
|
||||||
|
# *
|
||||||
|
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
# * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
# * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
# * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# * OF SUCH DAMAGE.
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Standard 16 colors display declaration
|
|
||||||
export DEFAULTFG="\e[0;39m"
|
|
||||||
export DEFAULTBG="\e[0;49m"
|
|
||||||
export DEFAULTCOL=${DEFAULTBG}${DEFAULTFG}
|
|
||||||
|
|
||||||
# Regular Colors
|
# ------------------------------------------------------------------------------
|
||||||
export Black='\e[0;30m'
|
# Color definitions
|
||||||
export Red='\e[0;31m'
|
set_colors()
|
||||||
export Green='\e[0;32m'
|
{
|
||||||
export Yellow='\e[0;33m'
|
# Standard 16 colors display declaration
|
||||||
export Blue='\e[0;34m'
|
export DEFAULTFG='\e[0;39m'
|
||||||
export Purple='\e[0;35m'
|
export DEFAULTBG='\e[0;49m'
|
||||||
export Cyan='\e[0;36m'
|
export DEFAULTCOL="${DEFAULTBG}${DEFAULTFG}"
|
||||||
export White='\e[0;37m'
|
export RESETCOL=$'\e[0m'
|
||||||
|
|
||||||
# Bold
|
# Regular Colors
|
||||||
export BBlack='\e[1;30m'
|
export Black='\e[0;30m'
|
||||||
export BRed='\e[1;31m'
|
export Red='\e[0;31m'
|
||||||
export BGreen='\e[1;32m'
|
export Green='\e[0;32m'
|
||||||
export BYellow='\e[1;33m'
|
export Yellow='\e[0;33m'
|
||||||
export BBlue='\e[1;34m'
|
export Blue='\e[0;34m'
|
||||||
export BPurple='\e[1;35m'
|
export Purple='\e[0;35m'
|
||||||
export BCyan='\e[1;36m'
|
export Cyan='\e[0;36m'
|
||||||
export BWhite='\e[1;37m'
|
export White='\e[0;37m'
|
||||||
|
|
||||||
# Underline
|
# Bold
|
||||||
export UBlack='\e[4;30m'
|
export BBlack='\e[1;30m'
|
||||||
export URed='\e[4;31m'
|
export BRed='\e[1;31m'
|
||||||
export UGreen='\e[4;32m'
|
export BGreen='\e[1;32m'
|
||||||
export UYellow='\e[4;33m'
|
export BYellow='\e[1;33m'
|
||||||
export UBlue='\e[4;34m'
|
export BBlue='\e[1;34m'
|
||||||
export UPurple='\e[4;35m'
|
export BPurple='\e[1;35m'
|
||||||
export UCyan='\e[4;36m'
|
export BCyan='\e[1;36m'
|
||||||
export UWhite='\e[4;37m'
|
export BWhite='\e[1;37m'
|
||||||
|
|
||||||
# Background
|
# Underline
|
||||||
export On_Black='\e[40m'
|
export UBlack='\e[4;30m'
|
||||||
export On_Red='\e[41m'
|
export URed='\e[4;31m'
|
||||||
export On_Green='\e[42m'
|
export UGreen='\e[4;32m'
|
||||||
export On_Yellow='\e[43m'
|
export UYellow='\e[4;33m'
|
||||||
export On_Blue='\e[44m'
|
export UBlue='\e[4;34m'
|
||||||
export On_Purple='\e[45m'
|
export UPurple='\e[4;35m'
|
||||||
export On_Cyan='\e[46m'
|
export UCyan='\e[4;36m'
|
||||||
export On_White='\e[47m'
|
export UWhite='\e[4;37m'
|
||||||
|
|
||||||
# High Intensity
|
# Background
|
||||||
export IBlack='\e[0;90m'
|
export On_Black='\e[40m'
|
||||||
export IRed='\e[0;91m'
|
export On_Red='\e[41m'
|
||||||
export IGreen='\e[0;92m'
|
export On_Green='\e[42m'
|
||||||
export IYellow='\e[0;93m'
|
export On_Yellow='\e[43m'
|
||||||
export IBlue='\e[0;94m'
|
export On_Blue='\e[44m'
|
||||||
export IPurple='\e[0;95m'
|
export On_Purple='\e[45m'
|
||||||
export ICyan='\e[0;96m'
|
export On_Cyan='\e[46m'
|
||||||
export IWhite='\e[0;97m'
|
export On_White='\e[47m'
|
||||||
|
|
||||||
# Bold High Intensity
|
# High Intensity
|
||||||
export BIBlack='\e[1;90m'
|
export IBlack='\e[0;90m'
|
||||||
export BIRed='\e[1;91m'
|
export IRed='\e[0;91m'
|
||||||
export BIGreen='\e[1;92m'
|
export IGreen='\e[0;92m'
|
||||||
export BIYellow='\e[1;93m'
|
export IYellow='\e[0;93m'
|
||||||
export BIBlue='\e[1;94m'
|
export IBlue='\e[0;94m'
|
||||||
export BIPurple='\e[1;95m'
|
export IPurple='\e[0;95m'
|
||||||
export BICyan='\e[1;96m'
|
export ICyan='\e[0;96m'
|
||||||
export BIWhite='\e[1;97m'
|
export IWhite='\e[0;97m'
|
||||||
|
|
||||||
# High Intensity backgrounds
|
# Bold High Intensity
|
||||||
export On_IBlack='\e[0;100m'
|
export BIBlack='\e[1;90m'
|
||||||
export On_IRed='\e[0;101m'
|
export BIRed='\e[1;91m'
|
||||||
export On_IGreen='\e[0;102m'
|
export BIGreen='\e[1;92m'
|
||||||
export On_IYellow='\e[0;103m'
|
export BIYellow='\e[1;93m'
|
||||||
export On_IBlue='\e[0;104m'
|
export BIBlue='\e[1;94m'
|
||||||
export On_IPurple='\e[0;105m'
|
export BIPurple='\e[1;95m'
|
||||||
export On_ICyan='\e[0;106m'
|
export BICyan='\e[1;96m'
|
||||||
export On_IWhite='\e[0;107m'
|
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'
|
||||||
|
}
|
||||||
|
export -f set_colors
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Display a message
|
# Display a message
|
||||||
# ------------------------------------------------------------------------------
|
# Usage: disp <type> <message>
|
||||||
|
# Types:
|
||||||
|
# I : info (green)
|
||||||
|
# W : warning (yellow)
|
||||||
|
# E : error (red)
|
||||||
|
# D : debug (cyan)
|
||||||
disp()
|
disp()
|
||||||
{
|
{
|
||||||
case $1 in
|
# Handle NO_COLOR: disable colors if set
|
||||||
|
local color_enabled=1
|
||||||
|
[[ -n $NO_COLOR ]] && color_enabled=0
|
||||||
|
|
||||||
|
case ${1^^} in
|
||||||
"I")
|
"I")
|
||||||
|
if [[ $color_enabled -eq 1 ]]; then
|
||||||
local heads="[ ${IGreen}info${DEFAULTFG} ]"
|
local heads="[ ${IGreen}info${DEFAULTFG} ]"
|
||||||
|
else
|
||||||
|
local heads="[ info ]"
|
||||||
|
fi
|
||||||
shift
|
shift
|
||||||
[[ -z $QUIET || $QUIET -ne 1 ]] && echo -e "${heads} $@"
|
[[ -z $QUIET || $QUIET -ne 1 ]] && \
|
||||||
|
printf "%b\n" "${heads} $*${RESETCOL}"
|
||||||
;;
|
;;
|
||||||
"W")
|
"W")
|
||||||
|
if [[ $color_enabled -eq 1 ]]; then
|
||||||
local heads="[ ${IYellow}Warning${DEFAULTFG} ]"
|
local heads="[ ${IYellow}Warning${DEFAULTFG} ]"
|
||||||
|
else
|
||||||
|
local heads="[ Warning ]"
|
||||||
|
fi
|
||||||
shift
|
shift
|
||||||
echo -e "${heads} $@" >&2
|
printf "%b\n" "${heads} $*${RESETCOL}" >&2
|
||||||
;;
|
;;
|
||||||
"E")
|
"E")
|
||||||
|
if [[ $color_enabled -eq 1 ]]; then
|
||||||
local heads="[ ${IRed}ERROR${DEFAULTFG} ]"
|
local heads="[ ${IRed}ERROR${DEFAULTFG} ]"
|
||||||
|
else
|
||||||
|
local heads="[ ERROR ]"
|
||||||
|
fi
|
||||||
shift
|
shift
|
||||||
echo -e "${heads} $@" >&2
|
printf "%b\n" "${heads} $*${RESETCOL}" >&2
|
||||||
;;
|
;;
|
||||||
"D")
|
"D")
|
||||||
local heads"[ ${ICyan}debug${DEFAULTFG} ]"
|
if [[ $color_enabled -eq 1 ]]; then
|
||||||
|
local heads="[ ${ICyan}debug${DEFAULTFG} ]"
|
||||||
|
else
|
||||||
|
local heads="[ debug ]"
|
||||||
|
fi
|
||||||
shift
|
shift
|
||||||
[[ -n $DEBUG && $DEBUG -gt 1 ]] && echo -e "${heads} $@"
|
[[ -n $DEBUG && $DEBUG -gt 1 ]] && \
|
||||||
|
printf "%b\n" "${heads} $*${RESETCOL}"
|
||||||
;;
|
;;
|
||||||
"*")
|
* )
|
||||||
local heads=""
|
[[ -z $QUIET || $QUIET -ne 1 ]] && \
|
||||||
[[ -z $QUIET || $QUIET -ne 1 ]] && echo -e "$@"
|
printf "%b\n" "$*"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
unset heads
|
|
||||||
}
|
}
|
||||||
export -f disp
|
export -f disp
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# Load disp section variables
|
||||||
|
load_conf disp
|
||||||
|
set_colors
|
||||||
|
|
||||||
|
# EOF
|
||||||
|
|||||||
@@ -1,179 +1,805 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# expandlist : treat wildcards in a file/directory list
|
# Copyright (c) 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
|
# Protected by the BSD3 license. Please read bellow for details.
|
||||||
|
#
|
||||||
|
# * Redistribution and use in source and binary forms,
|
||||||
|
# * with or without modification, are permitted provided
|
||||||
|
# * that the following conditions are met:
|
||||||
|
# *
|
||||||
|
# * Redistributions of source code must retain the above
|
||||||
|
# * copyright notice, this list of conditions and the
|
||||||
|
# * following disclaimer.
|
||||||
|
# *
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# * copyright notice, this list of conditions and the following
|
||||||
|
# * disclaimer in the documentation and/or other materials
|
||||||
|
# * provided with the distribution.
|
||||||
|
# *
|
||||||
|
# * Neither the name of the copyright holder nor the names
|
||||||
|
# * of any other contributors may be used to endorse or
|
||||||
|
# * promote products derived from this software without
|
||||||
|
# * specific prior written permission.
|
||||||
|
# *
|
||||||
|
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
# * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
# * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
# * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# * OF SUCH DAMAGE.
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Expand wildcards in a file/directory list and quote the results
|
||||||
|
# Usage: expandlist [options] <item1 [item2 ... itemN]>
|
||||||
expandlist()
|
expandlist()
|
||||||
{
|
{
|
||||||
local result=""
|
local separator="${EXPANDLIST_DEFAULT_SEPARATOR:- }"
|
||||||
for item in "$1"; do
|
local PARSED
|
||||||
for content in "$item"; do
|
PARSED=$(getopt -o hs:n --long help,separator:,newline -n 'expandlist' -- "$@")
|
||||||
result+="\"$content\" "
|
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||||
done
|
if [[ $? -ne 0 ]]; then
|
||||||
done
|
disp E "Invalid options, use \"expandlist --help\" to display usage."
|
||||||
echo $result
|
return 1
|
||||||
}
|
fi
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
|
||||||
|
while true; do
|
||||||
# ------------------------------------------------------------------------------
|
case "$1" in
|
||||||
# Clean a directory or a tree from temporary or backup files
|
-h|--help)
|
||||||
# ------------------------------------------------------------------------------
|
printf "expandlist: expand globs and wrap matched items in double quotes.\n\n"
|
||||||
clean ()
|
printf "Usage: expandlist [options] <item1 [item2 ... itemN]>\n\n"
|
||||||
{
|
printf "Options:\n"
|
||||||
for opt in $@ ; do
|
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||||
case $opt in
|
printf "\t-s, --separator SEP\tSet output separator (default: space)\n"
|
||||||
"-r"|"--recurs")
|
printf "\t-n, --newline\t\tUse a newline as separator\n"
|
||||||
local recursive=1
|
|
||||||
;;
|
|
||||||
|
|
||||||
"-h"|"--help")
|
|
||||||
echo "clean: erase backup files in the given directories."
|
|
||||||
echo
|
|
||||||
echo "Usage: clean [option] [directory1] [...[directoryX]]"
|
|
||||||
echo
|
|
||||||
echo "Options:"
|
|
||||||
echo " -h, --help Display that help screen"
|
|
||||||
echo " -r, --recurs Do a recursive cleaning"
|
|
||||||
echo " -f, --force Do not ask for confirmation (use with care)"
|
|
||||||
echo " -s, --shell Do nothing and display what will be executed"
|
|
||||||
echo
|
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
-s|--separator)
|
||||||
"-s"|"--shell")
|
separator="$2"
|
||||||
local outshell=1
|
shift 2
|
||||||
;;
|
;;
|
||||||
|
-n|--newline)
|
||||||
"-f"|"--force")
|
separator=$'\n'
|
||||||
local force=1
|
shift
|
||||||
;;
|
;;
|
||||||
|
--)
|
||||||
"-"*)
|
shift
|
||||||
disp E "Invalid option, use \"clean --help\" to display usage."
|
break
|
||||||
echo
|
|
||||||
return 1
|
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
local dirlist="$dirlist $opt"
|
disp E "Invalid options, use \"expandlist --help\" to display usage."
|
||||||
|
return 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
[[ ! $dirlist ]] && local dirlist=$(pwd)
|
local item="" result="" matched=0
|
||||||
|
shopt -s nullglob
|
||||||
|
|
||||||
[[ ! $recursive ]] && local findopt="-maxdepth 1"
|
for item in "$@"; do
|
||||||
[[ ! $force ]] && local rmopt="-i"
|
local expanded=()
|
||||||
unset recursive force
|
|
||||||
|
|
||||||
for dir in $dirlist; do
|
# True glob expansion when wildcards are present.
|
||||||
local dellist=$(find $dir $findopt -type f -name "*~" -o -name "#*#" \
|
if [[ "$item" == *'*'* || "$item" == *'?'* || "$item" == *'['* ]]; then
|
||||||
-o -name "*.bak" -o -name ".~*#")
|
# shellcheck disable=SC2206 # We actually want the word splitting
|
||||||
for f in $dellist; do
|
expanded=( $item )
|
||||||
if [[ ! $outshell ]]; then
|
|
||||||
rm $rmopt $f
|
|
||||||
else
|
else
|
||||||
echo "rm $rmopt $f"
|
expanded=( "$item" )
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ${#expanded[@]} -eq 0 ]]; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
for content in "${expanded[@]}"; do
|
||||||
|
if (( matched )); then
|
||||||
|
result+="$separator"
|
||||||
|
fi
|
||||||
|
result+="\"$content\""
|
||||||
|
matched=1
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
shopt -u nullglob
|
||||||
|
printf '%s\n' "$result"
|
||||||
|
}
|
||||||
|
export -f expandlist
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Clean a directory tree from temporary or backup files
|
||||||
|
# Usage: clean [options] [directory1] [...[directoryX]]
|
||||||
|
# Options:
|
||||||
|
# -h, --help: display help screen
|
||||||
|
# -r, --recurs: do a recursive cleaning
|
||||||
|
# -f, --force: do not ask for confirmation (use with care)
|
||||||
|
# -s, --shell: do nothing and display what will be executed
|
||||||
|
clean()
|
||||||
|
{
|
||||||
|
local recursive=${CLEAN_DEFAULT_RECURSIVE:-0} force=0 outshell=0
|
||||||
|
|
||||||
|
# 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
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-r|--recurs)
|
||||||
|
recursive=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
printf "clean: erase backup files in the given directories.\n\n"
|
||||||
|
printf "Usage: clean [option] [directory1] [...[directoryX]]\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-h, --help\t\tDisplay that help screen\n"
|
||||||
|
printf "\t-r, --recurs\t\tDo a recursive cleaning\n"
|
||||||
|
printf "\t-f, --force\t\tDo not ask for confirmation (use with care)\n"
|
||||||
|
printf "\t-s, --shell\t\tDo nothing and display what will be executed\n"
|
||||||
|
printf "\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-s|--shell)
|
||||||
|
outshell=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-f|--force)
|
||||||
|
force=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid parameter, use \"clean --help\" to display options list"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Handle remaining arguments as directories
|
||||||
|
local dirlist=("$@")
|
||||||
|
[[ ${#dirlist[@]} -eq 0 ]] && dirlist=(".")
|
||||||
|
|
||||||
|
local findopt=() rmopt=()
|
||||||
|
(( ! recursive )) && findopt=(-maxdepth 1)
|
||||||
|
(( ! force )) && rmopt=(-i)
|
||||||
|
|
||||||
|
for dir in "${dirlist[@]}"; do
|
||||||
|
find "$dir" "${findopt[@]}" -type f \( -name "*~" -o -name "#*#" -o -name "*.bak" -o -name ".~*#" \) -print0 |
|
||||||
|
while IFS= read -r -d '' f; do
|
||||||
|
if (( outshell )); then
|
||||||
|
if (( ${#rmopt[@]} )); then
|
||||||
|
printf 'rm %s -- "%s"\n' "${rmopt[*]}" "$f"
|
||||||
|
else
|
||||||
|
printf 'rm -- "%s"\n' "$f"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
rm "${rmopt[@]}" -- "$f"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
unset outshell dirlist dellist findopt rmopt
|
|
||||||
}
|
}
|
||||||
export -f clean
|
export -f clean
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Create a directory then goes inside
|
# Create a directory then goes inside
|
||||||
# ------------------------------------------------------------------------------
|
# Usage: mcd <directory>
|
||||||
mcd ()
|
mcd()
|
||||||
{
|
{
|
||||||
if [[ ! $# -eq 1 ]] ; then
|
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
|
||||||
disp E "Create a directory then goes inside."
|
printf "mcd: Create a directory and enter it.\n\n"
|
||||||
disp E "Usage: mcd <directory>"
|
printf "Usage: mcd <directory>\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! $# -eq 1 ]]; then
|
||||||
|
disp E "Missing parameter. Use \"mcd --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
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
|
return 1
|
||||||
fi
|
fi
|
||||||
mkdir -pv $1 && cd $1
|
|
||||||
}
|
}
|
||||||
export -f mcd
|
export -f mcd
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Rename all files in current directory to replace spaces with _
|
# Rename files and directories to replace spaces with another character
|
||||||
# ------------------------------------------------------------------------------
|
# Usage: rmspc [options]
|
||||||
rmspc ()
|
# Options:
|
||||||
|
# -h, --help: display help screen
|
||||||
|
# -r, --recursive: treat subdirectories of the given directory
|
||||||
|
# -c, --subst-char: change the replacement character (default is underscore)
|
||||||
|
# -v, --verbose: display more details (recursive mode only)
|
||||||
|
# -s, --shell: do nothing and display commands that would be executed
|
||||||
|
rmspc()
|
||||||
{
|
{
|
||||||
local lst=""
|
local recurs=0 verb=0 shell=0
|
||||||
for opt in $@ ; do
|
local substchar="${RMSPC_DEFAULT_CHAR:-_}" substchar_set=0
|
||||||
case $opt in
|
local mvopt=()
|
||||||
"-h"|"--help")
|
local PARSED
|
||||||
echo "rmspc: remove spaces from all filenames in current directories"
|
|
||||||
echo
|
PARSED=$(getopt -o hr:c::vs --long help,recursive,subst-char::,verbose,shell -n 'rmspc' -- "$@")
|
||||||
echo "Usage: rmspc [option]"
|
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||||
echo
|
if [[ $? -ne 0 ]]; then
|
||||||
echo "Options:"
|
disp E "Invalid options, use \"rmspc --help\" to display usage."
|
||||||
echo " -h, --help Display that help screen"
|
return 1
|
||||||
echo " -r, --recursive Treat subdirectories of the given directory"
|
fi
|
||||||
echo " -c, --subst-char Change the replacement character (default is underscore)"
|
eval set -- "$PARSED"
|
||||||
echo " -v, --verbose Display what is being done"
|
|
||||||
echo " -s, --shell Do nothing and display commands that would be executed"
|
while true; do
|
||||||
echo
|
case "$1" in
|
||||||
echo "Note: if the --subst-char option is given without parameters, spaces will be"
|
-h|--help)
|
||||||
echo " replaced with nothing (concatenation)."
|
printf "rmspc: remove spaces from all filenames in current directories\n\n"
|
||||||
echo
|
printf "Usage: rmspc [option]\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-h, --help\t\tDisplay that help screen\n"
|
||||||
|
printf "\t-r, --recursive\t\tTreat subdirectories of the given directory\n"
|
||||||
|
printf "\t-c, --subst-char\tChange the replacement character (default is underscore)\n"
|
||||||
|
printf "\t-v, --verbose\t\tDisplay more details (recursive mode only)\n"
|
||||||
|
printf "\t-s, --shell\t\tDo nothing and display commands that would be executed\n\n"
|
||||||
|
printf "Note: if the --subst-char option is given without parameters, spaces will be\n"
|
||||||
|
printf " replaced with nothing (concatenation).\n"
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
-r|--recursive)
|
||||||
"-r"|"--recursive")
|
recurs=1
|
||||||
local recurs=1
|
shift
|
||||||
;;
|
;;
|
||||||
|
-c|--subst-char)
|
||||||
"-c"?*|"--subst-char"?*)
|
substchar_set=1
|
||||||
if [[ $(echo $opt | grep "=") ]]; then
|
substchar="$2"
|
||||||
local substchar=$(echo "$opt" | cut -f 2- -d '=')
|
shift 2
|
||||||
else
|
|
||||||
local substchar='none'
|
|
||||||
fi
|
|
||||||
;;
|
;;
|
||||||
|
-v|--verbose)
|
||||||
"-v"|"--verbose")
|
verb=1
|
||||||
local verb=1
|
shift
|
||||||
;;
|
;;
|
||||||
|
-s|--shell)
|
||||||
"-s"|"--shell")
|
shell=1
|
||||||
local shell=1
|
shift
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
disp E "Invalid parameter, use \"rmspc --help\" to display options list"
|
disp E "Invalid parameter, use \"rmspc --help\" to display options list"
|
||||||
echo
|
|
||||||
return 1
|
return 1
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
[[ ! $substchar ]] && substchar="_"
|
[[ "$substchar" == "none" ]] && substchar=""
|
||||||
[[ $substchar == "none" ]] && local substchar=""
|
(( verb )) && mvopt=(-v)
|
||||||
[[ $verb ]] && local mvopt="-v"
|
|
||||||
|
|
||||||
|
shopt -s nullglob
|
||||||
for f in *; do
|
for f in *; do
|
||||||
[[ $recurs ]] && [[ -d "$f" ]] && (
|
if (( recurs )) && [[ -d "$f" ]]; then
|
||||||
[[ $verb ]] && disp I "Entering directory $(pwd)/$f ..."
|
(
|
||||||
local lastdir=$f
|
local lastdir=$f
|
||||||
pushd "$f" > /dev/null
|
(( verb )) && disp I "Entering directory $(pwd)/$f ..."
|
||||||
rmspc $@
|
pushd "$f" >/dev/null || return 1
|
||||||
popd > /dev/null
|
|
||||||
[[ $verb ]] && disp I "Leaving directory $(pwd)/$lastdir"
|
|
||||||
unset lastdir
|
|
||||||
)
|
|
||||||
|
|
||||||
if [[ $(echo $f | grep " ") ]]; then
|
if (( substchar_set )); then
|
||||||
local newf="${f// /${substchar}}"
|
rmspc ${recurs:+-r} -c "$substchar" ${verb:+-v} ${shell:+-s}
|
||||||
local command="mv $mvopt \"$f\" \"$newf\""
|
|
||||||
if [[ $shell ]]; then
|
|
||||||
echo $command
|
|
||||||
else
|
else
|
||||||
$command
|
rmspc ${recurs:+-r} ${verb:+-v} ${shell:+-s}
|
||||||
|
fi
|
||||||
|
|
||||||
|
popd >/dev/null || return 1
|
||||||
|
(( verb )) && disp I "Leaving directory $(pwd)/$lastdir"
|
||||||
|
)
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$f" == *" "* ]]; then
|
||||||
|
local newf="${f// /${substchar}}"
|
||||||
|
[[ "$f" == "$newf" ]] && continue
|
||||||
|
if (( shell )); then
|
||||||
|
if (( ${#mvopt[@]} )); then
|
||||||
|
printf 'mv %s -- "%s" "%s"\n' "${mvopt[*]}" "$f" "$newf"
|
||||||
|
else
|
||||||
|
printf 'mv -- "%s" "%s"\n' "$f" "$newf"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
mv "${mvopt[@]}" -- "$f" "$newf" || {
|
||||||
|
disp E "Failed renaming \"$f\" to \"$newf\"."
|
||||||
|
continue
|
||||||
|
}
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
unset lst substchar verb shell newf command mvopt
|
shopt -u nullglob
|
||||||
}
|
}
|
||||||
export -f rmspc
|
export -f rmspc
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Display statistics about a file tree
|
||||||
|
# Usage: file_stats [options] [path]
|
||||||
|
# Options:
|
||||||
|
# -H, --human Human readable sizes\n"
|
||||||
|
# -d, --details Display details (min/max/average/median)
|
||||||
|
# -m, --average Display only average size
|
||||||
|
# -M, --median Display only median size
|
||||||
|
# -c, --count Display only count of files
|
||||||
|
# -t, --total Display only total size
|
||||||
|
# -a, --all Display all stats in human readable format (shortcut for -H -d)
|
||||||
|
# -x, --ext [ext] Filter by extension (e.g. -x log for .log files)
|
||||||
|
# -X, --ext-list [list] Filter by multiple extensions (e.g. -X log,txt)
|
||||||
|
# --min [size] Minimum size (e.g., 10M)
|
||||||
|
# --max [size] Maximum size (e.g., 100M)
|
||||||
|
file_stats()
|
||||||
|
{
|
||||||
|
local human=0 details=0 only_avg=0 only_med=0 only_count=0 only_total=0
|
||||||
|
local path="." show_all=1 ext_filter="" ext_list="" min_size="" max_size=""
|
||||||
|
|
||||||
|
local PARSED
|
||||||
|
# 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
|
||||||
|
fi
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "Usage: file_stats [options] [path]\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-H, --human\t\tHuman readable sizes\n"
|
||||||
|
printf "\t-d, --details\t\tShow detailed histogram\n"
|
||||||
|
printf "\t-m, --average\t\tShow only average size\n"
|
||||||
|
printf "\t-M, --median\t\tShow only median size\n"
|
||||||
|
printf "\t-c, --count\t\tShow only file count\n"
|
||||||
|
printf "\t-t, --total\t\tShow only total size\n"
|
||||||
|
printf "\t-a, --all\t\tShow all (human + details)\n"
|
||||||
|
printf "\t-x, --ext [ext]\t\tFilter by extension\n"
|
||||||
|
printf "\t-X, --ext-list [list]\tFilter by comma-separated list\n"
|
||||||
|
printf "\t--min [size]\t\tMinimum size (e.g., 10M)\n"
|
||||||
|
printf "\t--max [size]\t\tMaximum size (e.g., 100M)\n"
|
||||||
|
return 0 ;;
|
||||||
|
-H|--human)
|
||||||
|
human=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-d|--details)
|
||||||
|
details=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-m|--average)
|
||||||
|
only_avg=1
|
||||||
|
show_all=0
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-M|--median)
|
||||||
|
only_med=1
|
||||||
|
show_all=0
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-c|--count)
|
||||||
|
only_count=1
|
||||||
|
show_all=0
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-t|--total)
|
||||||
|
only_total=1
|
||||||
|
show_all=0
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-a|--all)
|
||||||
|
human=1
|
||||||
|
details=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-x|--ext)
|
||||||
|
ext_filter="${2#.}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-X|--ext-list)
|
||||||
|
ext_list="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--min)
|
||||||
|
min_size="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--max)
|
||||||
|
max_size="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid option: $1"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ -n "$1" ]] && path="$1"
|
||||||
|
|
||||||
|
# Prepare find filters
|
||||||
|
local find_cmd=(find "$path" -type f)
|
||||||
|
|
||||||
|
# Single extension filter
|
||||||
|
if [[ -n "$ext_filter" ]]; then
|
||||||
|
find_cmd+=(-iname "*.$ext_filter")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extension list filter
|
||||||
|
if [[ -n "$ext_list" ]]; then
|
||||||
|
IFS=',' read -ra exts <<< "$ext_list"
|
||||||
|
find_cmd+=('(')
|
||||||
|
for i in "${!exts[@]}"; do
|
||||||
|
[[ $i -ne 0 ]] && find_cmd+=(-o)
|
||||||
|
find_cmd+=(-iname "*.${exts[$i]}")
|
||||||
|
done
|
||||||
|
find_cmd+=(')')
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Minimum/maximum size filters (evaluated in bytes)
|
||||||
|
if [[ -n "$min_size" ]]; then
|
||||||
|
find_cmd+=(-size +"$(numfmt --from=iec "$min_size")"c)
|
||||||
|
fi
|
||||||
|
if [[ -n "$max_size" ]]; then
|
||||||
|
find_cmd+=(-size -"$(( $(numfmt --from=iec "$max_size") + 1 ))"c)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Execution
|
||||||
|
"${find_cmd[@]}" -printf "%s\n" 2>/dev/null | sort -n | \
|
||||||
|
awk -v human="$human" -v details="$details" -v only_avg="$only_avg" \
|
||||||
|
-v only_med="$only_med" -v only_count="$only_count" \
|
||||||
|
-v only_total="$only_total" -v show_all="$show_all" -v path="$path" '
|
||||||
|
# Convert function
|
||||||
|
function human_readable(x) {
|
||||||
|
split("B KiB MiB GiB TiB", units)
|
||||||
|
i = 1
|
||||||
|
while (x >= 1024 && i < 5) {
|
||||||
|
x /= 1024
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return sprintf("%.2f %s", x, units[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
# Display function
|
||||||
|
function out(label, val, is_size) {
|
||||||
|
if (human == 1 && is_size == 1) val = human_readable(val)
|
||||||
|
printf "%-20s : %s\n", label, val
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
sizes[NR] = $1
|
||||||
|
total += $1
|
||||||
|
if (min == "" || $1 < min) min = $1
|
||||||
|
if (max == "" || $1 > max) max = $1
|
||||||
|
if ($1 == 0) bucket[0]++
|
||||||
|
else {
|
||||||
|
b = int(log($1)/log(1024))
|
||||||
|
bucket[b]++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
END {
|
||||||
|
count = NR
|
||||||
|
if (count == 0) {
|
||||||
|
print "No files found."
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
|
||||||
|
average = total / count
|
||||||
|
|
||||||
|
# Median calculation: exact using sorted array values
|
||||||
|
if (count % 2 == 1) {
|
||||||
|
median = sizes[(count + 1) / 2]
|
||||||
|
} else {
|
||||||
|
idx = count / 2
|
||||||
|
median = (sizes[idx] + sizes[idx + 1]) / 2
|
||||||
|
}
|
||||||
|
|
||||||
|
if (only_avg) out("Average size", average, 1)
|
||||||
|
else if (only_med) out("Median size", median, 1)
|
||||||
|
else if (only_count) out("Number of files", count, 0)
|
||||||
|
else if (only_total) out("Total size", total, 1)
|
||||||
|
else {
|
||||||
|
if (show_all || human || details) {
|
||||||
|
printf "Statistics for \"%s\"\n", path
|
||||||
|
printf "-------------------------\n"
|
||||||
|
}
|
||||||
|
out("Number of files", count, 0)
|
||||||
|
out("Total size", total, 1)
|
||||||
|
out("Average size", average, 1)
|
||||||
|
out("Median size", median, 1)
|
||||||
|
out("Minimum size", min, 1)
|
||||||
|
out("Maximum size", max, 1)
|
||||||
|
}
|
||||||
|
if (details) {
|
||||||
|
print "\nSize histogram:"
|
||||||
|
|
||||||
|
# Use a separate array for the loop to avoid collision
|
||||||
|
for (b in bucket) {
|
||||||
|
# Pre-calculate label parts
|
||||||
|
# 1024^0 = 1 (B), 1024^1 = 1K, etc.
|
||||||
|
low = (b == 0) ? 0 : (1024^b)
|
||||||
|
high = 1024^(b+1)
|
||||||
|
|
||||||
|
label = sprintf("%-9s – %-9s",
|
||||||
|
(b == 0) ? "0" : human_readable(low),
|
||||||
|
human_readable(high))
|
||||||
|
|
||||||
|
# We store buckets in an array, access them by index b
|
||||||
|
printf "%-25s : %6d fichiers\n", label, bucket[b]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}'
|
||||||
|
}
|
||||||
|
export -f file_stats
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Find the biggest files in a directory tree
|
||||||
|
# Usage: findbig [options] [directory]
|
||||||
|
# Options:
|
||||||
|
# -h : display help screen
|
||||||
|
# -d : display details (ls -l) for each file
|
||||||
|
# -x : do not cross filesystem boundaries
|
||||||
|
# -l : limit : number of files to return (default is 10)
|
||||||
|
findbig()
|
||||||
|
{
|
||||||
|
local details=0 limit=${FINDBIG_DEFAULT_LIMIT:-10} one_fs=0
|
||||||
|
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "findbig: Find the N biggest files in a directory tree.\n\n"
|
||||||
|
printf "Usage: findbig [options] [directory]\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||||
|
printf "\t-d, --details\t\tShow detailed file info (ls -ld)\n"
|
||||||
|
printf "\t-l, --limit N\t\tNumber of files to return (default: 10)\n"
|
||||||
|
printf "\t-x, --one-fs\t\tDo not cross filesystem boundaries\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-d|--details)
|
||||||
|
details=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-l|--limit)
|
||||||
|
limit="$2"
|
||||||
|
[[ "$limit" =~ ^[0-9]+$ ]] || {
|
||||||
|
disp E "Invalid limit: must be a positive integer."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-x|--one-fs)
|
||||||
|
one_fs=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid option: $1"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
local dir="${1:-.}"
|
||||||
|
|
||||||
|
# Prepare find arguments in an array for cleaner handling
|
||||||
|
local find_args=(-L "$dir")
|
||||||
|
(( one_fs )) && find_args+=(-xdev)
|
||||||
|
find_args+=(-type f)
|
||||||
|
|
||||||
|
# Logic: find files, print size and path, sort numeric reverse, take N
|
||||||
|
if (( details )); then
|
||||||
|
find "${find_args[@]}" -printf "%s %p\n" 2>/dev/null | sort -rn | head -n "$limit" |
|
||||||
|
while IFS= read -r line; do
|
||||||
|
local path="${line#* }"
|
||||||
|
ls -ld -- "$path"
|
||||||
|
done
|
||||||
|
else
|
||||||
|
find "${find_args[@]}" -printf "%s %p\n" 2>/dev/null | sort -rn | head -n "$limit"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
export -f findbig
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Find empty files in a directory tree
|
||||||
|
# Usage: findzero [options] [directory]
|
||||||
|
# Options:
|
||||||
|
# -h : display help screen
|
||||||
|
# -d : display details (ls -l) for each file
|
||||||
|
# -x : do not cross filesystem boundaries
|
||||||
|
# --delete : delete empty files and display their paths
|
||||||
|
findzero()
|
||||||
|
{
|
||||||
|
local delete=0 details=0 one_fs=0
|
||||||
|
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "findzero: Find or delete empty files in a directory tree.\n\n"
|
||||||
|
printf "Usage: findzero [options] [directory]\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||||
|
printf "\t-d, --details\t\tShow detailed file info (ls -ls)\n"
|
||||||
|
printf "\t-x, --one-fs\t\tDo not cross filesystem boundaries\n"
|
||||||
|
printf "\t--delete\t\tActually remove the empty files\n"
|
||||||
|
return 0 ;;
|
||||||
|
-d|--details)
|
||||||
|
details=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-x|--one-fs)
|
||||||
|
one_fs=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--delete)
|
||||||
|
delete=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid option: $1"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
local dir="${1:-.}"
|
||||||
|
local find_args=("-L" "$dir" "-type" "f" "-empty")
|
||||||
|
|
||||||
|
(( one_fs )) && find_args+=("-xdev")
|
||||||
|
|
||||||
|
# Execution logic
|
||||||
|
if (( delete )); then
|
||||||
|
disp W "Deleting empty files in $dir..."
|
||||||
|
find "${find_args[@]}" -delete -print
|
||||||
|
elif (( details )); then
|
||||||
|
find "${find_args[@]}" -ls
|
||||||
|
else
|
||||||
|
find "${find_args[@]}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
export -f findzero
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Find dead symbolic links in a directory tree
|
||||||
|
# Usage: finddead [options] [directory]
|
||||||
|
# Options:
|
||||||
|
# -h : display help screen
|
||||||
|
# -d : display details (ls -l) for each link
|
||||||
|
# -x : do not cross filesystem boundaries
|
||||||
|
# --delete : delete dead links and display their paths
|
||||||
|
finddead()
|
||||||
|
{
|
||||||
|
local delete=0 details=0 one_fs=0
|
||||||
|
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "finddead: Find or delete dead/broken symbolic links.\n\n"
|
||||||
|
printf "Usage: finddead [options] [directory]\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||||
|
printf "\t-d, --details\t\tShow detailed symlink info (ls -ls)\n"
|
||||||
|
printf "\t-x, --one-fs\t\tDo not cross filesystem boundaries\n"
|
||||||
|
printf "\t--delete\t\tActually remove the dead links\n"
|
||||||
|
return 0 ;;
|
||||||
|
-d|--details)
|
||||||
|
details=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-x|--one-fs)
|
||||||
|
one_fs=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--delete)
|
||||||
|
delete=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid option: $1"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
local dir="${1:-.}"
|
||||||
|
# -xtype l searches for links that do not point to an existing file
|
||||||
|
local find_args=("$dir" "-xtype" "l")
|
||||||
|
(( one_fs )) && find_args+=("-xdev")
|
||||||
|
|
||||||
|
# Execution logic
|
||||||
|
if (( delete )); then
|
||||||
|
disp W "Deleting dead symlinks in $dir..."
|
||||||
|
find "${find_args[@]}" -delete -print
|
||||||
|
elif (( details )); then
|
||||||
|
find "${find_args[@]}" -ls
|
||||||
|
else
|
||||||
|
find "${find_args[@]}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
export -f finddead
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
load_conf "filefct"
|
||||||
|
|
||||||
|
# EOF
|
||||||
|
|||||||
116
profile.d/fun.sh
Normal file
116
profile.d/fun.sh
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright (c) 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
|
# Protected by the BSD3 license. Please read bellow for details.
|
||||||
|
#
|
||||||
|
# * Redistribution and use in source and binary forms,
|
||||||
|
# * with or without modification, are permitted provided
|
||||||
|
# * that the following conditions are met:
|
||||||
|
# *
|
||||||
|
# * Redistributions of source code must retain the above
|
||||||
|
# * copyright notice, this list of conditions and the
|
||||||
|
# * following disclaimer.
|
||||||
|
# *
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# * copyright notice, this list of conditions and the following
|
||||||
|
# * disclaimer in the documentation and/or other materials
|
||||||
|
# * provided with the distribution.
|
||||||
|
# *
|
||||||
|
# * Neither the name of the copyright holder nor the names
|
||||||
|
# * of any other contributors may be used to endorse or
|
||||||
|
# * promote products derived from this software without
|
||||||
|
# * specific prior written permission.
|
||||||
|
# *
|
||||||
|
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
# * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
# * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
# * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# * OF SUCH DAMAGE.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Make non-IT peoples think you're busy doing something hard
|
||||||
|
# Usage: busy [options] [pattern]
|
||||||
|
# Options:
|
||||||
|
# --delay=<ms> : add a delay between each line output (milliseconds)
|
||||||
|
# pattern : the string to search for in the hexdump output (default is "ca fe")
|
||||||
|
busy()
|
||||||
|
{
|
||||||
|
local pattern="${BUSY_DEFAULT_PATTERN:-ca fe}" delay_ms="${BUSY_DEFAULT_DELAY:-0}"
|
||||||
|
|
||||||
|
local PARSED
|
||||||
|
# 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
|
||||||
|
fi
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "busy: Monitor /dev/urandom for a specific pattern.\n\n"
|
||||||
|
printf "Usage: busy [options] [pattern]\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||||
|
printf "\t-p, --pattern PATTERN\tHex pattern to search (default: \"ca fe\")\n"
|
||||||
|
printf "\t-d, --delay MS\t\tDelay between matches in milliseconds\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-p|--pattern)
|
||||||
|
pattern="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-d|--delay)
|
||||||
|
delay_ms="$2"
|
||||||
|
if ! [[ "$delay_ms" =~ ^[0-9]+$ ]]; then
|
||||||
|
disp E "Invalid delay: must be an integer (milliseconds)."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid option: $1"
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Convert milliseconds to seconds for 'sleep'
|
||||||
|
local delay_s
|
||||||
|
delay_s=$(awk "BEGIN{
|
||||||
|
printf \"%.3f\", $delay_ms / 1000 }")
|
||||||
|
|
||||||
|
# Monitor /dev/urandom
|
||||||
|
(
|
||||||
|
hexdump -C < /dev/urandom | grep -iF --line-buffered "$pattern" | \
|
||||||
|
while read -r line; do
|
||||||
|
echo "$line"
|
||||||
|
[[ $delay_ms -gt 0 ]] && sleep "$delay_s"
|
||||||
|
done
|
||||||
|
) & local sub_pid=$!
|
||||||
|
|
||||||
|
IFS= read -r -n 1 -s _ </dev/tty
|
||||||
|
kill -- -"$sub_pid" 2>/dev/null || kill "$sub_pid" 2>/dev/null
|
||||||
|
wait "$sub_pid" 2>/dev/null
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
load_conf "fun"
|
||||||
|
|
||||||
|
# EOF
|
||||||
@@ -1,33 +1,91 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright (c) 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
|
# Protected by the BSD3 license. Please read bellow for details.
|
||||||
|
#
|
||||||
|
# * Redistribution and use in source and binary forms,
|
||||||
|
# * with or without modification, are permitted provided
|
||||||
|
# * that the following conditions are met:
|
||||||
|
# *
|
||||||
|
# * Redistributions of source code must retain the above
|
||||||
|
# * copyright notice, this list of conditions and the
|
||||||
|
# * following disclaimer.
|
||||||
|
# *
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# * copyright notice, this list of conditions and the following
|
||||||
|
# * disclaimer in the documentation and/or other materials
|
||||||
|
# * provided with the distribution.
|
||||||
|
# *
|
||||||
|
# * Neither the name of the copyright holder nor the names
|
||||||
|
# * of any other contributors may be used to endorse or
|
||||||
|
# * promote products derived from this software without
|
||||||
|
# * specific prior written permission.
|
||||||
|
# *
|
||||||
|
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
# * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
# * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
# * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# * OF SUCH DAMAGE.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Display list of commands and general informations
|
# Display list of commands and general informations
|
||||||
# ------------------------------------------------------------------------------
|
# Usage: help
|
||||||
help()
|
help()
|
||||||
{
|
{
|
||||||
cat <<EOF
|
# shellcheck disable=SC2154 # color code in disp.sh
|
||||||
clean Erase backup files
|
# shellcheck disable=SC2059 # printf format is a color variable
|
||||||
dpkgs Search for the given package in the installed ones
|
printf "${BIWhite}Welcome to your profile! Here is a list of available commands:${DEFAULTCOL}\n\n"
|
||||||
gpid Give the list of PIDs for the given process name
|
printf "busy\t\tMonitor /dev/urandom for a hex pattern — look busy\n"
|
||||||
isipv4 Tell if the given IPv4 is valid
|
printf "check_updates\tCheck for new versions of profile\n"
|
||||||
isipv6 Tell if the given IPv6 is valid
|
printf "clean\t\tErase backup files in given directories, optionally recursive\n"
|
||||||
ku Kill process owned by users in parameter
|
printf "disp\t\tDisplay formatted info/warning/error/debug messages\n"
|
||||||
mcd Create a directory and go inside
|
printf "dwl\t\tDownload a URL using curl, wget, or fetch transparently\n"
|
||||||
meteo Display curent weather forecast for the configured city
|
printf "expandlist\tExpand glob expressions into a quoted, separated list\n"
|
||||||
ppg Display process matching the given parameter
|
printf "file_stats\tDisplay file size statistics for a path\n"
|
||||||
rain Let the rain fall
|
printf "findbig\t\tFind the biggest files in the given or current directory\n"
|
||||||
rmhost Remove host (IP and/or DNS name) for current known_host
|
printf "finddead\tFind dead symbolic links in the given or current directory\n"
|
||||||
rmspc Remove spaces from all the files in working directory
|
printf "findzero\tFind empty files in the given or current directory\n"
|
||||||
setc Set console language to C
|
printf "genpwd\t\tGenerate one or more random secure passwords with configurable constraints\n"
|
||||||
setfr Set console language to French
|
printf "gpid\t\tGive the list of PIDs matching the given process name(s)\n"
|
||||||
settrace Activate/deactivate call trace for script debugging
|
printf "isipv4\t\tTell if the given parameter is a valid IPv4 address\n"
|
||||||
setus Set console language to US English
|
printf "isipv6\t\tTell if the given parameter is a valid IPv6 address\n"
|
||||||
showinfo Show the welcoming baner with basic system information
|
printf "ku\t\tKill all processes owned by the given user name or ID\n"
|
||||||
ssr Do a root login to the given address
|
printf "matrix\t\tConsole screensaver with Matrix-style digital rain (binary, kana, ascii charset)\n"
|
||||||
taz Compress smartly the given files or directory
|
printf "mcd\t\tCreate a directory and immediately move into it\n"
|
||||||
utaz Uncompress all zip files in the given (or current) directory
|
printf "meteo\t\tDisplay weather forecast for the configured or given city\n"
|
||||||
ver Display version of your copy of profile
|
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"
|
||||||
|
|
||||||
Please use <command> --help to obtain usage details.
|
printf "\nPlease use <command> --help to obtain usage details.\n"
|
||||||
EOF
|
|
||||||
}
|
}
|
||||||
export -f help
|
export -f help
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# EOF
|
||||||
|
|||||||
@@ -1,55 +1,199 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright (c) 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
|
# Protected by the BSD3 license. Please read bellow for details.
|
||||||
|
#
|
||||||
|
# * Redistribution and use in source and binary forms,
|
||||||
|
# * with or without modification, are permitted provided
|
||||||
|
# * that the following conditions are met:
|
||||||
|
# *
|
||||||
|
# * Redistributions of source code must retain the above
|
||||||
|
# * copyright notice, this list of conditions and the
|
||||||
|
# * following disclaimer.
|
||||||
|
# *
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# * copyright notice, this list of conditions and the following
|
||||||
|
# * disclaimer in the documentation and/or other materials
|
||||||
|
# * provided with the distribution.
|
||||||
|
# *
|
||||||
|
# * Neither the name of the copyright holder nor the names
|
||||||
|
# * of any other contributors may be used to endorse or
|
||||||
|
# * promote products derived from this software without
|
||||||
|
# * specific prior written permission.
|
||||||
|
# *
|
||||||
|
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
# * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
# * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
# * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# * OF SUCH DAMAGE.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Show profile version
|
# Show profile version
|
||||||
# ------------------------------------------------------------------------------
|
# Usage: ver
|
||||||
ver ()
|
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
|
||||||
|
fi
|
||||||
|
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "ver: Display the current profile version.\nUsage: ver\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid options, use \"ver --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ -z $PROFVERSION ]] && \
|
||||||
|
disp W "No version defined. Profile is probably badly installed." && \
|
||||||
|
return 1
|
||||||
disp "Profile version $PROFVERSION."
|
disp "Profile version $PROFVERSION."
|
||||||
}
|
}
|
||||||
export -f ver
|
export -f ver
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Display weather of the given city (or default one)
|
# Display weather for the given city (or the default one)
|
||||||
# ------------------------------------------------------------------------------
|
# Usage: meteo [city1 city2 ...]
|
||||||
meteo ()
|
meteo()
|
||||||
{
|
{
|
||||||
cities=$@
|
local PARSED
|
||||||
[[ $# -eq 0 ]] && local cities=$DEFAULT_CITY
|
|
||||||
|
|
||||||
for city in $cities; do
|
PARSED=$(getopt -o h --long help -n 'meteo' -- "$@")
|
||||||
curl https://wttr.in/$city || disp E "Failed fetching datas for $city."
|
# 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
|
||||||
|
fi
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "meteo: Fetch weather data.\n"
|
||||||
|
printf "Usage: meteo [city1 city2 ...]\n"
|
||||||
|
printf "If no city is provided, the default city from configuration will be used.\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid options, use \"meteo --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
local cities=("$@")
|
||||||
|
local city="" encoded=""
|
||||||
|
[[ $# -eq 0 ]] && cities=("${METEO_DEFAULT_CITY:-}")
|
||||||
|
|
||||||
|
if [[ ${#cities[@]} -eq 0 || -z "${cities[0]}" ]]; then
|
||||||
|
disp E "No city given and METEO_DEFAULT_CITY is not set. Use 'meteo <city>'."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for city in "${cities[@]}"; do
|
||||||
|
encoded=$(urlencode "$city")
|
||||||
|
dwl "https://wttr.in/$encoded" || \
|
||||||
|
disp E "Failed to fetch weather data for $city."
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
export -f meteo
|
export -f meteo
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Display system general information
|
# Display system general information
|
||||||
# ------------------------------------------------------------------------------
|
# Usage: showinfo
|
||||||
showinfo()
|
showinfo()
|
||||||
{
|
{
|
||||||
echo -e "\n"
|
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
|
||||||
|
fi
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "showinfo: Display system information (hostname, kernel, uptime and fetch output when available).\n"
|
||||||
|
printf "Usage: showinfo\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid options, use \"showinfo --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
local hostname_str
|
||||||
|
local figopt=()
|
||||||
|
hostname_str="$(hostname)"
|
||||||
|
|
||||||
|
printf "\n"
|
||||||
if command -v figlet >/dev/null 2>&1; then
|
if command -v figlet >/dev/null 2>&1; then
|
||||||
if [[ -s /usr/share/figlet/ansi_shadow.flf ]]; then
|
[[ -s /usr/share/figlet/ansi_shadow.flf ]] && \
|
||||||
local figopt="-f ansi_shadow"
|
figopt=(-f ansi_shadow)
|
||||||
fi
|
figlet -k "${figopt[@]}" "$hostname_str"
|
||||||
figlet -k $(hostname) $figopt
|
|
||||||
else
|
else
|
||||||
echo "$(hostname -f)"
|
printf "%s\n" "$hostname_str"
|
||||||
fi
|
fi
|
||||||
echo ""
|
|
||||||
|
printf "\n"
|
||||||
if command -v neofetch >/dev/null 2>&1; then
|
if command -v neofetch >/dev/null 2>&1; then
|
||||||
neofetch
|
neofetch
|
||||||
|
elif command -v fastfetch >/dev/null 2>&1; then
|
||||||
|
fastfetch
|
||||||
else
|
else
|
||||||
(
|
(
|
||||||
if [[ -s /etc/os-release ]]; then
|
if [[ -s /etc/os-release ]]; then
|
||||||
|
# shellcheck disable=SC1091
|
||||||
. /etc/os-release
|
. /etc/os-release
|
||||||
echo "$NAME $VERSION"
|
printf "%s %s\n" "$NAME" "$VERSION"
|
||||||
else
|
else
|
||||||
cat /proc/version
|
cat /proc/version
|
||||||
fi
|
fi
|
||||||
echo "Uptime: $(uptime)"
|
printf "Uptime: %s\n" "$(uptime -p)"
|
||||||
)
|
)
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
export -f showinfo
|
export -f showinfo
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
load_conf info
|
||||||
|
# EOF
|
||||||
|
|||||||
@@ -1,35 +1,182 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Change locale to French
|
# Copyright (c) 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
|
# Protected by the BSD3 license. Please read bellow for details.
|
||||||
|
#
|
||||||
|
# * Redistribution and use in source and binary forms,
|
||||||
|
# * with or without modification, are permitted provided
|
||||||
|
# * that the following conditions are met:
|
||||||
|
# *
|
||||||
|
# * Redistributions of source code must retain the above
|
||||||
|
# * copyright notice, this list of conditions and the
|
||||||
|
# * following disclaimer.
|
||||||
|
# *
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# * copyright notice, this list of conditions and the following
|
||||||
|
# * disclaimer in the documentation and/or other materials
|
||||||
|
# * provided with the distribution.
|
||||||
|
# *
|
||||||
|
# * Neither the name of the copyright holder nor the names
|
||||||
|
# * of any other contributors may be used to endorse or
|
||||||
|
# * promote products derived from this software without
|
||||||
|
# * specific prior written permission.
|
||||||
|
# *
|
||||||
|
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
# * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
# * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
# * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# * OF SUCH DAMAGE.
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
setfr ()
|
|
||||||
|
locale_check()
|
||||||
{
|
{
|
||||||
# Set fr locale definitions
|
locale -a | grep -qx "$1" || {
|
||||||
export LANG=fr_FR.UTF-8
|
disp W "Locale '$1' is not installed on this system."
|
||||||
export LC_MESSAGES=fr_FR.UTF-8
|
return 1
|
||||||
export LC_ALL=fr_FR.UTF-8
|
}
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
export -f setfr
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Change locale to C standard
|
# Change locale to the given one in parameter
|
||||||
|
# Usage: setlocale <locale>
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "setlocale: Configure system environment locale variables.\n\n"
|
||||||
|
printf "Usage: setlocale <locale>\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf " -h, --help Display this help screen\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid options, use \"setlocale --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
local loc=$1
|
||||||
|
[[ -z $loc ]] && disp E "No locale specified." && return 1
|
||||||
|
|
||||||
|
locale_check "$loc" || return 1
|
||||||
|
|
||||||
|
export LANG=$loc
|
||||||
|
export LC_MESSAGES=$loc
|
||||||
|
export LC_TIME=$loc
|
||||||
|
export LC_NUMERIC=$loc
|
||||||
|
export LC_MONETARY=$loc
|
||||||
|
export LC_COLLATE=$loc
|
||||||
|
export LC_CTYPE=$loc
|
||||||
|
|
||||||
|
disp I "Locale set to $loc."
|
||||||
|
}
|
||||||
|
export -f setlocale
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
setc ()
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Special case : change locale to C standard
|
||||||
|
# Usage: setc
|
||||||
|
setc()
|
||||||
{
|
{
|
||||||
# Locale definitions
|
# Locale definitions
|
||||||
export LANG=C
|
|
||||||
export LC_MESSAGES=C
|
|
||||||
export LC_ALL=C
|
export LC_ALL=C
|
||||||
|
disp I "Locale changed to standard C (POSIX)."
|
||||||
}
|
}
|
||||||
export -f setc
|
export -f setc
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Change locale to US (needed by Steam)
|
# Build dynamic locale shortcuts from SET_LOCALE
|
||||||
# ------------------------------------------------------------------------------
|
# Expected format:
|
||||||
setus ()
|
# SET_LOCALE="fr:fr_FR.UTF-8,us:en_US.UTF-8,es:es_ES.UTF-8"
|
||||||
|
# This creates functions:
|
||||||
|
# setfr, setus, setes, ...
|
||||||
|
build_locale_shortcuts()
|
||||||
{
|
{
|
||||||
# Locale definitions
|
local cfg="${SET_LOCALE:-}"
|
||||||
export LANG=en_US.UTF-8
|
local item="" alias="" loc="" fname=""
|
||||||
export LC_MESSAGES=en_US.UTF-8
|
local -a locale_items=()
|
||||||
export LC_ALL=en_US.UTF-8
|
|
||||||
|
[[ -z "$cfg" ]] && return 0
|
||||||
|
|
||||||
|
IFS=',' read -r -a locale_items <<< "$cfg"
|
||||||
|
for item in "${locale_items[@]}"; do
|
||||||
|
# Trim surrounding spaces
|
||||||
|
item="${item#"${item%%[![:space:]]*}"}"
|
||||||
|
item="${item%"${item##*[![:space:]]}"}"
|
||||||
|
|
||||||
|
[[ -z "$item" ]] && continue
|
||||||
|
|
||||||
|
if [[ "$item" != *:* ]]; then
|
||||||
|
disp W "Ignoring invalid SET_LOCALE entry: '$item' (expected alias:locale)."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
alias="${item%%:*}"
|
||||||
|
loc="${item#*:}"
|
||||||
|
|
||||||
|
# Trim alias/locale spaces
|
||||||
|
alias="${alias#"${alias%%[![:space:]]*}"}"
|
||||||
|
alias="${alias%"${alias##*[![:space:]]}"}"
|
||||||
|
loc="${loc#"${loc%%[![:space:]]*}"}"
|
||||||
|
loc="${loc%"${loc##*[![:space:]]}"}"
|
||||||
|
|
||||||
|
# Validate alias for safe function names
|
||||||
|
if [[ ! "$alias" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then
|
||||||
|
disp W "Ignoring unsafe locale alias '$alias' in SET_LOCALE."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ -z "$loc" ]] && {
|
||||||
|
disp W "Ignoring empty locale for alias '$alias' in SET_LOCALE."
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fname="set${alias}"
|
||||||
|
|
||||||
|
# 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\"; }"
|
||||||
|
# shellcheck disable=SC2163
|
||||||
|
export -f "$fname"
|
||||||
|
done
|
||||||
|
|
||||||
|
unset cfg item alias loc fname locale_items
|
||||||
}
|
}
|
||||||
export -f setus
|
export -f build_locale_shortcuts
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
load_conf lang
|
||||||
|
build_locale_shortcuts
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# EOF
|
||||||
|
|||||||
307
profile.d/net.sh
307
profile.d/net.sh
@@ -1,37 +1,177 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright (c) 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
|
# Protected by the BSD3 license. Please read bellow for details.
|
||||||
|
#
|
||||||
|
# * Redistribution and use in source and binary forms,
|
||||||
|
# * with or without modification, are permitted provided
|
||||||
|
# * that the following conditions are met:
|
||||||
|
# *
|
||||||
|
# * Redistributions of source code must retain the above
|
||||||
|
# * copyright notice, this list of conditions and the
|
||||||
|
# * following disclaimer.
|
||||||
|
# *
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# * copyright notice, this list of conditions and the following
|
||||||
|
# * disclaimer in the documentation and/or other materials
|
||||||
|
# * provided with the distribution.
|
||||||
|
# *
|
||||||
|
# * Neither the name of the copyright holder nor the names
|
||||||
|
# * of any other contributors may be used to endorse or
|
||||||
|
# * promote products derived from this software without
|
||||||
|
# * specific prior written permission.
|
||||||
|
# *
|
||||||
|
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
# * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
# * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
# * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# * OF SUCH DAMAGE.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Download a resource using curl, wget, or fetch.
|
||||||
|
# Usage: dwl <url> [output_file]
|
||||||
|
dwl()
|
||||||
|
{
|
||||||
|
case "$1" in
|
||||||
|
--help|-h)
|
||||||
|
echo "Usage: dwl <url> [output_file]"
|
||||||
|
echo "Downloads a resource using curl, wget, or fetch."
|
||||||
|
echo ""
|
||||||
|
echo "Arguments:"
|
||||||
|
echo " url The full URL to download (http/https/ftp)."
|
||||||
|
echo " output_file (Optional) Path to save the file. If omitted, prints to stdout."
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
"")
|
||||||
|
echo "Error: URL argument is missing." >&2
|
||||||
|
echo "Try 'get_resource --help' for usage." >&2
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
case "$1" in
|
||||||
|
http://*|https://*|ftp://*) ;;
|
||||||
|
*)
|
||||||
|
echo "Error: '$1' does not look like a valid URL. Must start with http://, https://, or ftp://" >&2
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
local url="$1"
|
||||||
|
local output="$2"
|
||||||
|
|
||||||
|
# Honour preferred tool from configuration; fall back to auto-detection.
|
||||||
|
local preferred="${DWL_PREFERRED_TOOL:-}"
|
||||||
|
|
||||||
|
_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 || {
|
||||||
|
echo "Error: preferred download tool '$preferred' is not installed." >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
case "$preferred" in
|
||||||
|
curl) _try_curl ;;
|
||||||
|
wget) _try_wget ;;
|
||||||
|
fetch) _try_fetch ;;
|
||||||
|
*)
|
||||||
|
echo "Error: DWL_PREFERRED_TOOL '$preferred' is not supported (use curl, wget or fetch)." >&2
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
elif command -v curl >/dev/null 2>&1; then
|
||||||
|
_try_curl
|
||||||
|
elif command -v wget >/dev/null 2>&1; then
|
||||||
|
_try_wget
|
||||||
|
elif command -v fetch >/dev/null 2>&1; then
|
||||||
|
_try_fetch
|
||||||
|
else
|
||||||
|
echo "Error: No download utility (curl, wget, or fetch) found." >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
unset -f _try_curl _try_wget _try_fetch
|
||||||
|
}
|
||||||
|
export -f dwl
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Determine if parameter is a valid IPv4 address
|
# Determine if parameter is a valid IPv4 address
|
||||||
# ------------------------------------------------------------------------------
|
# Usage: isipv4 <ip_address>
|
||||||
isipv4 ()
|
isipv4()
|
||||||
{
|
{
|
||||||
# Set up local variables
|
# Set up local variables
|
||||||
local ip=$1
|
local ip=$1
|
||||||
|
[[ -z $ip ]] && return 1
|
||||||
|
|
||||||
# Start with a regex format test
|
# Start with a regex format test (four octets)
|
||||||
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
if [[ $ip =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
||||||
local old_ifs=$IFS
|
local old_ifs=$IFS
|
||||||
IFS="."
|
IFS='.'
|
||||||
ip=($ip)
|
read -r -a ip_arr <<< "$ip"
|
||||||
IFS=$old_ifs
|
IFS=$old_ifs
|
||||||
if [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
|
|
||||||
&& ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]; then
|
# Ensure each octet is between 0 and 255
|
||||||
if [[ -t 1 ]]; then
|
local oct
|
||||||
disp "The given IPv4 is valid."
|
for oct in "${ip_arr[@]}"; do
|
||||||
|
# Reject leading plus/minus or empty entries
|
||||||
|
if [[ -z $oct || $oct =~ [^0-9] ]]; then
|
||||||
|
[[ -t 1 ]] && disp "The given parameter is NOT a valid IPv4."
|
||||||
|
return 1
|
||||||
fi
|
fi
|
||||||
|
if (( oct > 255 )); then
|
||||||
|
[[ -t 1 ]] && disp "The given parameter is NOT a valid IPv4."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
[[ -t 1 ]] && disp "The given IPv4 is valid."
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
fi
|
|
||||||
if [[ -t 1 ]]; then
|
[[ -t 1 ]] && disp "The given parameter is NOT a valid IPv4."
|
||||||
disp "The given parameter is NOT a valid IPv4."
|
|
||||||
fi
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
export -f isipv4
|
export -f isipv4
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Determine if parameter is a valid IPv4 address
|
# Determine if parameter is a valid IPv6 address
|
||||||
# ------------------------------------------------------------------------------
|
# Usage: isipv6 <ip_address>
|
||||||
isipv6 ()
|
isipv6()
|
||||||
{
|
{
|
||||||
local ip="$1"
|
local ip="$1"
|
||||||
local regex='^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$'
|
local regex='^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$'
|
||||||
@@ -47,3 +187,136 @@ isipv6 ()
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
export -f isipv6
|
export -f isipv6
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Encode a string so it can be used as a URL parameter
|
||||||
|
# Usage: urlencode <string>
|
||||||
|
urlencode()
|
||||||
|
{
|
||||||
|
local LANG=C
|
||||||
|
local str="$*"
|
||||||
|
local length="${#str}"
|
||||||
|
for (( i = 0; i < length; i++ )); do
|
||||||
|
local c="${str:i:1}"
|
||||||
|
case "$c" in
|
||||||
|
[a-zA-Z0-9.~_-]) printf '%s' "$c" ;;
|
||||||
|
' ') printf '+' ;;
|
||||||
|
*) printf '%%%02X' "'$c" #| cut -d' ' -f2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
export -f urlencode
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Fetch and display external IP information
|
||||||
|
# Usage: myextip [-i|--ip] [-s|--isp] [-l|--loc] [-c|--coord]
|
||||||
|
# If no option is provided, all information will be displayed.
|
||||||
|
# Options:
|
||||||
|
# -h, --help Display help screen
|
||||||
|
# -i, --ip Display only the external IP address
|
||||||
|
# -s, --isp Display only the ISP name
|
||||||
|
# -l, --loc Display only the location (city, region, country)
|
||||||
|
# -c, --coord Display only the coordinates (latitude, longitude)
|
||||||
|
# -a, --as Display only the Autonomous System (AS) information
|
||||||
|
# -R, --raw Display raw JSON response
|
||||||
|
myextip()
|
||||||
|
{
|
||||||
|
local show_ip=false show_isp=false show_loc=false
|
||||||
|
local show_coord=false show_as=false show_raw=false
|
||||||
|
local all=true
|
||||||
|
|
||||||
|
# Parse arguments
|
||||||
|
while [[ "$#" -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-i|--ip)
|
||||||
|
show_ip=true
|
||||||
|
all=false
|
||||||
|
;;
|
||||||
|
-s|--isp)
|
||||||
|
show_isp=true
|
||||||
|
all=false
|
||||||
|
;;
|
||||||
|
-l|--loc)
|
||||||
|
show_loc=true
|
||||||
|
all=false
|
||||||
|
;;
|
||||||
|
-c|--coord)
|
||||||
|
show_coord=true
|
||||||
|
all=false
|
||||||
|
;;
|
||||||
|
-a|--as)
|
||||||
|
show_as=true
|
||||||
|
all=false
|
||||||
|
;;
|
||||||
|
-R|--raw)
|
||||||
|
all=false
|
||||||
|
show_raw=true
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
printf "Fetch and display external IP information.\n\n"
|
||||||
|
printf "Usage: myextip [-i|--ip] [-s|--isp] [-l|--loc] [-c|--coord] [-a|--as] [-R|--raw]\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-h, --help\tDisplay this help screen\n"
|
||||||
|
printf "\t-i, --ip\tDisplay only the external IP address\n"
|
||||||
|
printf "\t-s, --isp\tDisplay only the ISP name\n"
|
||||||
|
printf "\t-l, --loc\tDisplay only the location (city, region, country)\n"
|
||||||
|
printf "\t-c, --coord\tDisplay only the coordinates (latitude, longitude)\n"
|
||||||
|
printf "\t-a, --as\tDisplay only the Autonomous System (AS) information\n"
|
||||||
|
printf "\t-R, --raw\tDisplay raw JSON response\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Unknown option: $1, use \"myextip --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
# Fetch data. Allow overriding endpoint via MYEXTIP_DEFAULT_URL config key.
|
||||||
|
local api_url="${MYEXTIP_DEFAULT_URL:-https://ip-api.com/json/}"
|
||||||
|
|
||||||
|
local response
|
||||||
|
if ! response=$(dwl "$api_url"); then
|
||||||
|
disp E "Failed to fetch external IP information from $api_url"
|
||||||
|
return 2
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Parse with jq when available and when raw wasn't requested. The jq filter
|
||||||
|
# is tolerant to field-name differences between providers (ip-api / ipinfo).
|
||||||
|
if command -v jq >/dev/null 2>&1 && [[ "$show_raw" != true ]]; then
|
||||||
|
echo "$response" | jq -r --argjson all "$all" --argjson ip "$show_ip" \
|
||||||
|
--argjson isp "$show_isp" --argjson loc "$show_loc" \
|
||||||
|
--argjson coord "$show_coord" --argjson as "$show_as" '
|
||||||
|
[
|
||||||
|
(if $all or $ip then "IP Address : \(.query // .ip)" else empty end),
|
||||||
|
(if $all or $isp then "ISP : \(.isp // .org)" else empty end),
|
||||||
|
(if $all or $loc then
|
||||||
|
("Location : " + ((.city // "") + (if .city then ", " else "" end) + (if .regionName then .regionName else .region end) + (if .country then ", " + .country else "" end)))
|
||||||
|
else empty end),
|
||||||
|
(if $all or $coord then (if (.lat and .lon) then "Coordinates: \(.lat), \(.lon)" elif .loc then "Coordinates: \(.loc)" else empty end) else empty end),
|
||||||
|
(if $all or $as then "AS : \(.as // .org)" else empty end)
|
||||||
|
] | .[]'
|
||||||
|
else
|
||||||
|
[[ "$show_raw" != true ]] && disp W "jq is not installed, displaying raw JSON response."
|
||||||
|
echo "$response"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
export -f myextip
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# EOF
|
||||||
|
|
||||||
|
load_conf "net"
|
||||||
|
|
||||||
|
# EOF
|
||||||
|
|||||||
@@ -1,42 +1,181 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright (c) 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
|
# Protected by the BSD3 license. Please read bellow for details.
|
||||||
|
#
|
||||||
|
# * Redistribution and use in source and binary forms,
|
||||||
|
# * with or without modification, are permitted provided
|
||||||
|
# * that the following conditions are met:
|
||||||
|
# *
|
||||||
|
# * Redistributions of source code must retain the above
|
||||||
|
# * copyright notice, this list of conditions and the
|
||||||
|
# * following disclaimer.
|
||||||
|
# *
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# * copyright notice, this list of conditions and the following
|
||||||
|
# * disclaimer in the documentation and/or other materials
|
||||||
|
# * provided with the distribution.
|
||||||
|
# *
|
||||||
|
# * Neither the name of the copyright holder nor the names
|
||||||
|
# * of any other contributors may be used to endorse or
|
||||||
|
# * promote products derived from this software without
|
||||||
|
# * specific prior written permission.
|
||||||
|
# *
|
||||||
|
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
# * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
# * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
# * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# * OF SUCH DAMAGE.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 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
|
# Look for a package within installed one
|
||||||
# ------------------------------------------------------------------------------
|
# Usage: pkgs <string>
|
||||||
dpkgs ()
|
pkgs()
|
||||||
{
|
{
|
||||||
local count=0
|
local ignore_case=${PKGS_DEFAULT_IGNORE_CASE:-0}
|
||||||
for opt in $@ ; do
|
|
||||||
case $opt in
|
local PARSED
|
||||||
"-h"|"--help")
|
PARSED=$(getopt -o hi --long help,ignore-case -n 'pkgs' -- "$@")
|
||||||
echo "dpkgs: look for an installed package by it's name."
|
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||||
echo
|
if [[ $? -ne 0 ]]; then
|
||||||
echo "Usage: dpkgs <string>"
|
disp E "Invalid options, use \"pkgs --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "pkgs: Look for an installed package by its name.\n\n"
|
||||||
|
printf "Usage: pkgs [options] <string>\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-h, --help\tDisplay this help screen\n"
|
||||||
|
printf "\t-i, --ignore-case\tIgnore case distinctions\n"
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
-i|--ignore-case)
|
||||||
"-"*)
|
ignore_case=1
|
||||||
echo "Invalid option, use \"dpkgs --help\" to display usage."
|
shift
|
||||||
echo
|
;;
|
||||||
return 1
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
;;
|
;;
|
||||||
|
|
||||||
*)
|
*)
|
||||||
local pkg=$1 && shift
|
disp E "Invalid option: $1"
|
||||||
count=$(( $count + 1 ))
|
|
||||||
[[ $count -gt 1 ]] &&
|
|
||||||
disp E "Please specify a package name, without space, eventually partial." &&
|
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
[[ $count -lt 1 ]] &&
|
|
||||||
disp E "Please specify a package name, without space, eventually partial." &&
|
local pkg="$1"
|
||||||
|
[[ -z "$pkg" ]] && {
|
||||||
|
disp E "Please specify a package name, without space, eventually partial."
|
||||||
return 1
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
[[ -x /usr/sbin/dpkg ]] &&
|
# Build grep command
|
||||||
disp E "dpkg command seems unavialable." &&
|
local grep_opt=""
|
||||||
|
(( ignore_case )) && grep_opt="-i"
|
||||||
|
|
||||||
|
local pkgmgr
|
||||||
|
pkgmgr=$(_get_pkgmgr) || {
|
||||||
|
disp E "No usable package manager could be detected on this system."
|
||||||
return 2
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
dpkg -l | grep $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 dpkgs
|
export -f pkgs
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
load_conf "packages"
|
||||||
|
|
||||||
|
# EOF
|
||||||
|
|||||||
@@ -1,40 +1,256 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright (c) 2013-2022 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
|
# Protected by the BSD3 license. Please read bellow for details.
|
||||||
|
#
|
||||||
|
# * Redistribution and use in source and binary forms,
|
||||||
|
# * with or without modification, are permitted provided
|
||||||
|
# * that the following conditions are met:
|
||||||
|
# *
|
||||||
|
# * Redistributions of source code must retain the above
|
||||||
|
# * copyright notice, this list of conditions and the
|
||||||
|
# * following disclaimer.
|
||||||
|
# *
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# * copyright notice, this list of conditions and the following
|
||||||
|
# * disclaimer in the documentation and/or other materials
|
||||||
|
# * provided with the distribution.
|
||||||
|
# *
|
||||||
|
# * Neither the name of the copyright holder nor the names
|
||||||
|
# * of any other contributors may be used to endorse or
|
||||||
|
# * promote products derived from this software without
|
||||||
|
# * specific prior written permission.
|
||||||
|
# *
|
||||||
|
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
# * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
# * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
# * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# * OF SUCH DAMAGE.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Search processes matching the given string
|
# Search processes matching the given string
|
||||||
# ------------------------------------------------------------------------------
|
# Usage: ppg <string>
|
||||||
ppg ()
|
ppg()
|
||||||
{
|
{
|
||||||
ps -edf | grep $@ | grep -v "grep $@"
|
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
|
||||||
|
printf "ppg: Search processes matching the given string.\n\n"
|
||||||
|
printf "Usage: ppg <string>\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ -z "$1" ]]; then
|
||||||
|
disp E "Usage: ppg <string>"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local pattern="$*"
|
||||||
|
|
||||||
|
if command -v pgrep >/dev/null 2>&1; then
|
||||||
|
pgrep -af -- "$pattern"
|
||||||
|
return $?
|
||||||
|
fi
|
||||||
|
|
||||||
|
ps -ef | awk -v pattern="$pattern" '
|
||||||
|
NR == 1 {
|
||||||
|
print
|
||||||
|
next
|
||||||
|
}
|
||||||
|
index($0, pattern) {
|
||||||
|
print
|
||||||
|
matched = 1
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
exit matched ? 0 : 1
|
||||||
|
}
|
||||||
|
'
|
||||||
}
|
}
|
||||||
export -f ppg
|
export -f ppg
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# List processes owned by a specific user
|
||||||
|
# Usage: ppu <username>
|
||||||
|
ppu()
|
||||||
|
{
|
||||||
|
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
|
||||||
|
printf "ppu: List processes owned by a specific user.\n\n"
|
||||||
|
printf "Usage: ppu <username>\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ -z "$1" ]]; then
|
||||||
|
disp E "Usage: ppu <username>"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# -u lists processes for a specific user
|
||||||
|
# -o provides a clean, standard output format
|
||||||
|
ps -u "$1" -o "${PPU_DEFAULT_FORMAT:-pid,user,%cpu,%mem,start,time,command}"
|
||||||
|
}
|
||||||
|
export -f ppu
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# List processes by exact command name (no path/parameters)
|
||||||
|
# Usage: ppn <command_name>
|
||||||
|
ppn()
|
||||||
|
{
|
||||||
|
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
|
||||||
|
printf "ppn: List processes by exact command name (no path/parameters).\n\n"
|
||||||
|
printf "Usage: ppn <command_name>\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ -z "$1" ]]; then
|
||||||
|
disp E "Usage: ppn <command_name>"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# -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
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Get PID list of the given process name
|
# Get PID list of the given process name
|
||||||
# ------------------------------------------------------------------------------
|
# Usage: ppid <process_name [process_name2 ...]>
|
||||||
gpid ()
|
gpid()
|
||||||
{
|
{
|
||||||
[[ $UID -eq 0 ]] && local psopt="-A"
|
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
|
||||||
[[ $# -eq 1 ]] && local single=1
|
printf "gpid: Get PID list of the given process name.\n\n"
|
||||||
for pid in $@; do
|
printf "Usage: gpid <process_name [process_name2 ...]>\n\n"
|
||||||
local result=$(ps $psopt | grep $pid | awk '{print $1}' | sed "s/\n/ /")
|
printf "Options:\n"
|
||||||
if [[ $single ]]; then
|
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||||
[[ $result ]] && echo "${result//$'\n'/ }"
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ -z "$1" ]]; then
|
||||||
|
disp E "Usage: gpid <process_name [process_name2 ...]>"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
local single=0
|
||||||
|
local found=0
|
||||||
|
local proc_name result
|
||||||
|
|
||||||
|
[[ $# -eq 1 ]] && single=1
|
||||||
|
|
||||||
|
for proc_name in "$@"; do
|
||||||
|
result=""
|
||||||
|
|
||||||
|
if command -v pgrep >/dev/null 2>&1; then
|
||||||
|
result=$(pgrep -d ' ' -x -- "$proc_name")
|
||||||
else
|
else
|
||||||
[[ $result ]] && echo "$pid: ${result//$'\n'/ }"
|
result=$(ps -eo pid=,comm= | awk -v proc="$proc_name" '
|
||||||
|
$2 == proc {
|
||||||
|
if (out != "") {
|
||||||
|
out = out " "
|
||||||
|
}
|
||||||
|
out = out $1
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
print out
|
||||||
|
}
|
||||||
|
')
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ -z "$result" ]] && continue
|
||||||
|
|
||||||
|
found=1
|
||||||
|
if (( single )); then
|
||||||
|
echo "$result"
|
||||||
|
else
|
||||||
|
echo "$proc_name: $result"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
[[ $result ]] || return 1
|
|
||||||
|
(( found )) || return 1
|
||||||
}
|
}
|
||||||
export -f gpid
|
export -f gpid
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Kill all processes owned by the given users
|
# Kill all processes owned by the given users (kill user)
|
||||||
# ------------------------------------------------------------------------------
|
# Usage: ku <username1 [username2 ...]>
|
||||||
ku ()
|
ku()
|
||||||
{
|
{
|
||||||
for u in $@; do
|
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
|
||||||
killall -u $u
|
printf "ku: Kill all processes owned by the given users.\n\n"
|
||||||
|
printf "Usage: ku <username1 [username2 ...]>\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ -z "$1" ]]; then
|
||||||
|
disp E "Usage: ku <username1 [username2 ...]>"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
for u in "$@"; do
|
||||||
|
if ! id "$u" >/dev/null 2>&1; then
|
||||||
|
disp E "User '$u' does not exist."
|
||||||
|
return 1
|
||||||
|
else
|
||||||
|
killall ${KU_DEFAULT_SIGNAL:+-${KU_DEFAULT_SIGNAL}} -u "$u"
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
export -f ku
|
export -f ku
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Kill all children of a process then the process (kill tree)
|
||||||
|
# Usage: kt <pid> [kill_options]
|
||||||
|
kt()
|
||||||
|
{
|
||||||
|
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
|
||||||
|
printf "kt: Kill all children of a process then the process (kill tree).\n\n"
|
||||||
|
printf "Usage: kt <pid> [kill_options]\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ -z "$1" ]]; then
|
||||||
|
disp E "Usage: kt <pid>"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local parent_pid="$1"
|
||||||
|
shift
|
||||||
|
if [[ "$parent_pid" == "0" || "$parent_pid" == "1" ]]; then
|
||||||
|
disp E "Safety abort: Refusing to kill PID $parent_pid (system critical)."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
local children_pids
|
||||||
|
children_pids=$(pgrep -P "$parent_pid")
|
||||||
|
|
||||||
|
for pid in $children_pids; do
|
||||||
|
kt "$pid" "$@" || break
|
||||||
|
done
|
||||||
|
kill "$@" "$parent_pid"
|
||||||
|
}
|
||||||
|
export -f kt
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
load_conf "processes"
|
||||||
|
|
||||||
|
# EOF
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,258 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright (c) 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
|
# Protected by the BSD3 license. Please read bellow for details.
|
||||||
|
#
|
||||||
|
# * Redistribution and use in source and binary forms,
|
||||||
|
# * with or without modification, are permitted provided
|
||||||
|
# * that the following conditions are met:
|
||||||
|
# *
|
||||||
|
# * Redistributions of source code must retain the above
|
||||||
|
# * copyright notice, this list of conditions and the
|
||||||
|
# * following disclaimer.
|
||||||
|
# *
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# * copyright notice, this list of conditions and the following
|
||||||
|
# * disclaimer in the documentation and/or other materials
|
||||||
|
# * provided with the distribution.
|
||||||
|
# *
|
||||||
|
# * Neither the name of the copyright holder nor the names
|
||||||
|
# * of any other contributors may be used to endorse or
|
||||||
|
# * promote products derived from this software without
|
||||||
|
# * specific prior written permission.
|
||||||
|
# *
|
||||||
|
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
# * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
# * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
# * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# * OF SUCH DAMAGE.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Parse a prompt theme file safely — it is NEVER sourced or executed.
|
||||||
|
# Two categories of keys are accepted:
|
||||||
|
# PROMPT_COLOR_* — prompt slot colours (TIME_FG, BAR_BG, …)
|
||||||
|
# Standard colour variables from disp.sh (Blue, On_IBlack, …) — allows a
|
||||||
|
# theme to redefine the palette used everywhere in the shell session.
|
||||||
|
# Allowed value forms:
|
||||||
|
# $ColorName or ${ColorName} — colour variable from disp.sh (resolved by
|
||||||
|
# indirection via ${!varname})
|
||||||
|
# \e[...m or \033[...m — raw ANSI escape literal (single block)
|
||||||
|
# Any other key or value is rejected with a warning.
|
||||||
|
# Usage: load_theme <theme_name_or_path> [theme_dir]
|
||||||
|
# theme_name_or_path : bare name (e.g. "dark") or an explicit path.
|
||||||
|
# theme_dir : directory to search for bare names; defaults to
|
||||||
|
# $MYPATH/profile.d/themes. Overridable via
|
||||||
|
# PROMPT_THEME_DIR.
|
||||||
|
load_theme()
|
||||||
|
{
|
||||||
|
local theme_name="$1"
|
||||||
|
local theme_dir="${2:-${PROMPT_THEME_DIR:-$MYPATH/profile.d/themes}}"
|
||||||
|
local theme_file=""
|
||||||
|
|
||||||
|
[[ -z "$theme_name" ]] && return 0
|
||||||
|
|
||||||
|
if [[ "$theme_name" == /* || "$theme_name" == */* ]]; then
|
||||||
|
theme_file="$theme_name"
|
||||||
|
else
|
||||||
|
theme_file="$theme_dir/${theme_name}.theme"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -f "$theme_file" || ! -r "$theme_file" ]]; then
|
||||||
|
printf "[ Warning ] load_theme: theme file not found: %s\n" "$theme_file" >&2
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---- Key whitelist: prompt slots ----------------------------------------
|
||||||
|
local -A _lth_allowed=(
|
||||||
|
[PROMPT_COLOR_TIME_FG]=1 [PROMPT_COLOR_TIME_BG]=1
|
||||||
|
[PROMPT_COLOR_BAR_BG]=1
|
||||||
|
[PROMPT_COLOR_OK_FG]=1 [PROMPT_COLOR_OK_MARK]=1
|
||||||
|
[PROMPT_COLOR_ERR_BG]=1 [PROMPT_COLOR_ERR_FG]=1 [PROMPT_COLOR_ERR_MARK]=1
|
||||||
|
[PROMPT_COLOR_ROOT_FG]=1 [PROMPT_COLOR_USER_FG]=1
|
||||||
|
[PROMPT_COLOR_DIR_FG]=1
|
||||||
|
)
|
||||||
|
|
||||||
|
# ---- Colour variable names exported by disp.sh --------------------------
|
||||||
|
local _lth_color_re
|
||||||
|
_lth_color_re='Black|Red|Green|Yellow|Blue|Purple|Cyan|White'
|
||||||
|
_lth_color_re+='|BBlack|BRed|BGreen|BYellow|BBlue|BPurple|BCyan|BWhite'
|
||||||
|
_lth_color_re+='|UBlack|URed|UGreen|UYellow|UBlue|UPurple|UCyan|UWhite'
|
||||||
|
_lth_color_re+='|On_Black|On_Red|On_Green|On_Yellow|On_Blue|On_Purple|On_Cyan|On_White'
|
||||||
|
_lth_color_re+='|IBlack|IRed|IGreen|IYellow|IBlue|IPurple|ICyan|IWhite'
|
||||||
|
_lth_color_re+='|BIBlack|BIRed|BIGreen|BIYellow|BIBlue|BIPurple|BICyan|BIWhite'
|
||||||
|
_lth_color_re+='|On_IBlack|On_IRed|On_IGreen|On_IYellow|On_IBlue|On_IPurple|On_ICyan|On_IWhite'
|
||||||
|
_lth_color_re+='|DEFAULTFG|DEFAULTBG|DEFAULTCOL|RESETCOL'
|
||||||
|
|
||||||
|
# ---- Key whitelist: standard colour vars (same list as above) -----------
|
||||||
|
local _lth_cn
|
||||||
|
for _lth_cn in \
|
||||||
|
Black Red Green Yellow Blue Purple Cyan White \
|
||||||
|
BBlack BRed BGreen BYellow BBlue BPurple BCyan BWhite \
|
||||||
|
UBlack URed UGreen UYellow UBlue UPurple UCyan UWhite \
|
||||||
|
On_Black On_Red On_Green On_Yellow On_Blue On_Purple On_Cyan On_White \
|
||||||
|
IBlack IRed IGreen IYellow IBlue IPurple ICyan IWhite \
|
||||||
|
BIBlack BIRed BIGreen BIYellow BIBlue BIPurple BICyan BIWhite \
|
||||||
|
On_IBlack On_IRed On_IGreen On_IYellow On_IBlue On_IPurple On_ICyan On_IWhite \
|
||||||
|
DEFAULTFG DEFAULTBG DEFAULTCOL RESETCOL; do
|
||||||
|
_lth_allowed[$_lth_cn]=1
|
||||||
|
done
|
||||||
|
unset _lth_cn
|
||||||
|
|
||||||
|
# ERE: safe colour reference $Name or ${Name}
|
||||||
|
local _lth_ref_re='^\$\{?('"$_lth_color_re"')\}?$'
|
||||||
|
|
||||||
|
# ERE: raw ANSI escape literal \e[...m or \033[...m
|
||||||
|
local _lth_ansi_re='^(\\e|\\033)\[[0-9;]*m$'
|
||||||
|
|
||||||
|
# ---- Line parser ---------------------------------------------------------
|
||||||
|
local _lth_line _lth_key _lth_value _lth_varname _lth_lineno=0
|
||||||
|
|
||||||
|
while IFS= read -r _lth_line || [[ -n "$_lth_line" ]]; do
|
||||||
|
((_lth_lineno++))
|
||||||
|
_lth_line="${_lth_line%$'\r'}" # strip CR
|
||||||
|
_lth_line="${_lth_line#"${_lth_line%%[![:space:]]*}"}" # ltrim
|
||||||
|
_lth_line="${_lth_line%"${_lth_line##*[![:space:]]}"}" # rtrim
|
||||||
|
[[ -z "$_lth_line" || "$_lth_line" == '#'* ]] && continue # blank/comment
|
||||||
|
[[ "$_lth_line" == 'export '* ]] && _lth_line="${_lth_line#export }" # strip prefix
|
||||||
|
|
||||||
|
if [[ "$_lth_line" != *=* ]]; then
|
||||||
|
printf "[ Warning ] load_theme: %s:%d: not a key=value pair, ignoring.\n" \
|
||||||
|
"$theme_file" "$_lth_lineno" >&2
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
_lth_key="${_lth_line%%=*}"
|
||||||
|
_lth_value="${_lth_line#*=}"
|
||||||
|
_lth_key="${_lth_key#"${_lth_key%%[![:space:]]*}"}"
|
||||||
|
_lth_key="${_lth_key%"${_lth_key##*[![:space:]]}"}" # trim key
|
||||||
|
|
||||||
|
if [[ -z "${_lth_allowed[$_lth_key]+x}" ]]; then
|
||||||
|
printf "[ Warning ] load_theme: %s:%d: key '%s' is not allowed, ignoring.\n" \
|
||||||
|
"$theme_file" "$_lth_lineno" "$_lth_key" >&2
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 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
|
||||||
|
_lth_varname="${_lth_value#\$}"
|
||||||
|
_lth_varname="${_lth_varname#\{}"
|
||||||
|
_lth_varname="${_lth_varname%\}}"
|
||||||
|
export "$_lth_key"="${!_lth_varname}"
|
||||||
|
elif [[ "$_lth_value" =~ $_lth_ansi_re ]]; then
|
||||||
|
# Raw ANSI escape literal — accept as-is
|
||||||
|
export "$_lth_key"="$_lth_value"
|
||||||
|
else
|
||||||
|
printf "[ Warning ] load_theme: %s:%d: invalid value for '%s', ignoring.\n" \
|
||||||
|
"$theme_file" "$_lth_lineno" "$_lth_key" >&2
|
||||||
|
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
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# timer_* functions : internal timing function for prompt
|
# timer_* functions : internal timing function for prompt
|
||||||
# ------------------------------------------------------------------------------
|
# Usage: timer_now
|
||||||
|
# This function returns the current time in nanoseconds since the epoch. It
|
||||||
|
# first tries to use the %N format specifier for nanoseconds, but if that is
|
||||||
|
# not supported (e.g., on older systems), it falls back to seconds.
|
||||||
function timer_now
|
function timer_now
|
||||||
{
|
{
|
||||||
date +%s%N
|
date +%s%N 2>/dev/null || date +%s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Usage: timer_start
|
||||||
|
# This function initializes the timer_start variable with the current time in
|
||||||
|
# nanoseconds. It is used to measure the elapsed time for the prompt.
|
||||||
function timer_start
|
function timer_start
|
||||||
{
|
{
|
||||||
timer_start=${timer_start:-$(timer_now)}
|
timer_start=${timer_start:-$(timer_now)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Usage: timer_stop
|
||||||
|
# This function calculates the elapsed time since timer_start and formats it
|
||||||
|
# into a human-readable string with appropriate units (us, ms, s, m, h
|
||||||
function timer_stop
|
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 us=$((delta_us % 1000))
|
||||||
local ms=$(((delta_us / 1000) % 1000))
|
local ms=$(((delta_us / 1000) % 1000))
|
||||||
local s=$(((delta_us / 1000000) % 60))
|
local s=$(((delta_us / 1000000) % 60))
|
||||||
@@ -39,52 +278,84 @@ function timer_stop
|
|||||||
}
|
}
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Function triguered internaly by bash : defining prompt
|
# Function triggered internally by bash : defining prompt
|
||||||
# ------------------------------------------------------------------------------
|
# Usage: set_prompt
|
||||||
set_prompt ()
|
# This function is called by bash before displaying the prompt. It sets the
|
||||||
|
# PS1 variable to a custom prompt that includes the exit status of the last
|
||||||
|
# command, the elapsed time of the last command, and the current user and host.
|
||||||
|
set_prompt()
|
||||||
{
|
{
|
||||||
Last_Command=$? # Must come first!
|
local Last_Command=$? # Must come first!
|
||||||
Blue='\[\e[0;34m\]'
|
local FancyX='\342\234\227'
|
||||||
White='\[\e[01;37m\]'
|
local Checkmark='\342\234\223'
|
||||||
Yellow='\[\e[01;93m\]'
|
|
||||||
Red='\[\e[01;31m\]'
|
|
||||||
Green='\[\e[01;32m\]'
|
|
||||||
OnGrey='\[\e[47m\]'
|
|
||||||
OnRed='\[\e[41m\]'
|
|
||||||
OnBlue='\[\e[44m\]'
|
|
||||||
ICyan='\[\e[0;96m\]'
|
|
||||||
Default='\[\e[00m\]'
|
|
||||||
FancyX='\342\234\227'
|
|
||||||
Checkmark='\342\234\223'
|
|
||||||
|
|
||||||
# Begin with time
|
# Resolve theme/config colours with hardcoded fallbacks
|
||||||
PS1="\[\e[s$Blue$OnGrey [ \t ] $OnBlue"
|
local _time_fg="${PROMPT_COLOR_TIME_FG:-$Blue}"
|
||||||
|
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:-$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:-$BGreen}"
|
||||||
|
local _dir_fg="${PROMPT_COLOR_DIR_FG:-$ICyan}"
|
||||||
|
|
||||||
# Add a bright white exit status for the last command
|
# 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}\]"
|
||||||
|
|
||||||
# If it was successful, print a green check mark. Otherwise, print
|
# Add exit status of the last command.
|
||||||
# a red X.
|
# If it was successful, print a green check mark. Otherwise, print a red X.
|
||||||
if [[ $Last_Command == 0 ]]; then
|
if [[ $Last_Command == 0 ]]; then
|
||||||
PS1+="$White$OnBlue [ \$Last_Command "
|
PS1+="\[${_ok_fg}${_bar_bg}\] [ $Last_Command "
|
||||||
PS1+="$Green$Checkmark "
|
PS1+="\[${_ok_mark}${_bar_bg}\]${Checkmark} "
|
||||||
else
|
# Add the elapsed time, then close the status section and return to bar bg.
|
||||||
PS1+="$White$OnRed [ \$Last_Command "
|
|
||||||
PS1+="$Yellow$FancyX "
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Add the ellapsed time and current date
|
|
||||||
timer_stop
|
timer_stop
|
||||||
PS1+="($timer_show)$White ] $OnBlue "
|
PS1+="($timer_show)\[${_ok_fg}${_bar_bg}\] ] "
|
||||||
|
|
||||||
# If root, just print the host in red. Otherwise, print the current user
|
|
||||||
# and host in green.
|
|
||||||
if [[ $EUID -eq 0 ]]; then
|
|
||||||
PS1+="$Red\\u$Green@\\h"
|
|
||||||
else
|
else
|
||||||
PS1+="$Green\\u@\\h"
|
PS1+="\[${_err_fg}${_err_bg}\] [ $Last_Command "
|
||||||
|
PS1+="\[${_err_mark}${_err_bg}\]${FancyX} "
|
||||||
|
timer_stop
|
||||||
|
PS1+="($timer_show)\[${_err_fg}${_err_bg}\] ] "
|
||||||
fi
|
fi
|
||||||
PS1+="\e[K\e[u$Default\n"
|
|
||||||
# Print the working directory and prompt marker in blue, and reset
|
|
||||||
# the text color to the default.
|
# If root, print the host in root colour. Otherwise use user colour.
|
||||||
PS1+="$ICyan\\w \\\$$Default "
|
if [[ $EUID -eq 0 ]]; then
|
||||||
|
PS1+="\[${_root_fg}${_bar_bg}\] \\u\[${_user_fg}${_bar_bg}\]@\\h"
|
||||||
|
else
|
||||||
|
PS1+="\[${_user_fg}${_bar_bg}\] \\u@\\h"
|
||||||
|
fi
|
||||||
|
PS1+="\[\e[K\e[u\]\[$RESETCOL\]\n"
|
||||||
|
# Print the working directory and prompt marker, then reset colour.
|
||||||
|
PS1+="\[${_dir_fg}\]\\w \\\$\[$RESETCOL\] "
|
||||||
}
|
}
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Theme and configuration loading.
|
||||||
|
# Precedence (lowest → highest):
|
||||||
|
# 1. Hardcoded fallbacks in set_prompt
|
||||||
|
# 2. Theme file (PROMPT_THEME key from [prompt] section)
|
||||||
|
# 3. Individual PROMPT_COLOR_* overrides in [prompt] section
|
||||||
|
#
|
||||||
|
# CONF_prompt is already populated by parse_conf (run in profile.sh before
|
||||||
|
# modules are sourced). We extract PROMPT_THEME and PROMPT_THEME_DIR from the
|
||||||
|
# raw associative array now so load_theme can run before load_conf "prompt"
|
||||||
|
# exports remaining keys. That way any PROMPT_COLOR_* value set explicitly in
|
||||||
|
# [prompt] wins over the same variable set by the theme file.
|
||||||
|
_pt_theme="${CONF_prompt[PROMPT_THEME]:-}"
|
||||||
|
_pt_dir="${CONF_prompt[PROMPT_THEME_DIR]:-}"
|
||||||
|
[[ -n "$_pt_theme" ]] && load_theme "$_pt_theme" ${_pt_dir:+"$_pt_dir"}
|
||||||
|
unset _pt_theme _pt_dir
|
||||||
|
|
||||||
|
load_conf "prompt"
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# EOF
|
||||||
|
|||||||
560
profile.d/pwd.sh
Executable file → Normal file
560
profile.d/pwd.sh
Executable file → Normal file
@@ -1,151 +1,483 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright (c) 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
|
# Protected by the BSD3 license. Please read bellow for details.
|
||||||
|
#
|
||||||
|
# * Redistribution and use in source and binary forms,
|
||||||
|
# * with or without modification, are permitted provided
|
||||||
|
# * that the following conditions are met:
|
||||||
|
# *
|
||||||
|
# * Redistributions of source code must retain the above
|
||||||
|
# * copyright notice, this list of conditions and the
|
||||||
|
# * following disclaimer.
|
||||||
|
# *
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# * copyright notice, this list of conditions and the following
|
||||||
|
# * disclaimer in the documentation and/or other materials
|
||||||
|
# * provided with the distribution.
|
||||||
|
# *
|
||||||
|
# * Neither the name of the copyright holder nor the names
|
||||||
|
# * of any other contributors may be used to endorse or
|
||||||
|
# * promote products derived from this software without
|
||||||
|
# * specific prior written permission.
|
||||||
|
# *
|
||||||
|
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
# * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
# * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
# * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# * OF SUCH DAMAGE.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# genpwd : generate a password with different criteria
|
# genpwd : generate a password with different criteria
|
||||||
# default 16 car with up and low car, symbol and number
|
# Usage: genpwd [options] [--extracars=<cars>] [--length=<n>] [nb_passwd]
|
||||||
|
# Options:
|
||||||
|
# -h, --help Display that help screen
|
||||||
|
# -s, --nosymbols Exclude symbols
|
||||||
|
# -n, --nonumbers Exclude numbers
|
||||||
|
# -u, --noup Exclude uppercase letters
|
||||||
|
# -l, --nolow Exclude lowercase letters
|
||||||
|
# -e=<c>, --extracars=<c>
|
||||||
|
# Add the given caracters to the possible caracter list
|
||||||
|
# -L=<n>, --length=<n>
|
||||||
|
# Set length of the password (default is 16)
|
||||||
|
# -o=<n>, --occurences=<n>
|
||||||
|
# Set the maximum occurences of a same caracter (default is 2)
|
||||||
# The function is very slow on Windows
|
# The function is very slow on Windows
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
genpwd()
|
genpwd()
|
||||||
{
|
{
|
||||||
local length=16
|
local length=${GENPWD_DEFAULT_LENGTH:-16}
|
||||||
local occurs=2 # Bug, if set to 1, seems to be ignored
|
local occurs=${GENPWD_DEFAULT_OCCURS:-2}
|
||||||
local symb=1 maj=1 min=1 numb=1
|
local symb=${GENPWD_DEFAULT_SYMBOLS:-1}
|
||||||
local nbpwd=1
|
local maj=${GENPWD_DEFAULT_UPPERCASE:-1}
|
||||||
|
local min=${GENPWD_DEFAULT_LOWERCASE:-1}
|
||||||
|
local numb=${GENPWD_DEFAULT_NUMBERS:-1}
|
||||||
|
local nbpwd=${GENPWD_DEFAULT_COUNT:-1}
|
||||||
|
local extcar=""
|
||||||
|
|
||||||
for opt in $@; do
|
local PARSED
|
||||||
case $opt in
|
PARSED=$(getopt -o hsnule:L:o: --long \
|
||||||
"-h"|"--help")
|
help,nosymbols,nonumbers,noup,nolow,extracars:,length:,occurences:,occurrences: \
|
||||||
echo "genpwd: generate a secure random password."
|
-n 'genpwd' -- "$@")
|
||||||
echo
|
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||||
echo "Usage: genpwd [options] [--extracars=<cars>] [--length=<n>] [nb_passwd]"
|
if [[ $? -ne 0 ]]; then return 1; fi
|
||||||
echo
|
eval set -- "$PARSED"
|
||||||
echo "Options:"
|
|
||||||
echo " -h, --help Display that help screen"
|
while true; do
|
||||||
echo " -s, --nosymbols Exclude symbols"
|
case "$1" in
|
||||||
echo " -n, --nonumbers Exclude numbers"
|
-h|--help)
|
||||||
echo " -u, --noup Exclude uppercase letters"
|
printf "genpwd: Generate random password(s).\n\n"
|
||||||
echo " -l, --nolow Exclude lowercase letters"
|
printf "Usage: genpwd [options] [nb_passwd]\n\n"
|
||||||
echo " -e=<c>, --extracars=<c>"
|
printf "Options:\n"
|
||||||
echo " Add the given caracters to the possible caracter list"
|
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||||
echo " -L=<n>, --length=<n>"
|
printf "\t-s, --nosymbols\t\tExclude symbols\n"
|
||||||
echo " Set length of the password (default is $length)"
|
printf "\t-n, --nonumbers\t\tExclude numbers\n"
|
||||||
echo " -o=<n>, --occurences=<n>"
|
printf "\t-u, --noup\t\tExclude uppercase letters\n"
|
||||||
echo " Set the maximum occurences of a same caracter (default is $occurs)"
|
printf "\t-l, --nolow\t\tExclude lowercase letters\n"
|
||||||
echo
|
printf "\t-e, --extracars <c>\tAdd characters to the pool\n"
|
||||||
echo "If the --extracars parameter is given, at least one of the given caracter will"
|
printf "\t-L, --length <n>\tSet password length (default: 16)\n"
|
||||||
echo "be used in the final password."
|
printf "\t-o, --occurences <n>\tMax occurrences per character (default: 2)\n"
|
||||||
echo
|
|
||||||
echo "Please note that some caracters might be interpreted by Bash or Awk programs,"
|
|
||||||
echo "and thus, cannot be used without provoquing errors. Those identified caracters"
|
|
||||||
echo "are :"
|
|
||||||
echo ' * ? \ $ { }'
|
|
||||||
echo
|
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
"-s"|"--nosymbols")
|
-s|--nosymbols)
|
||||||
symb=0
|
symb=0
|
||||||
|
shift
|
||||||
;;
|
;;
|
||||||
"-n"|"--nonumbers")
|
-n|--nonumbers)
|
||||||
numb=0
|
numb=0
|
||||||
|
shift
|
||||||
;;
|
;;
|
||||||
"-u"|"--noup")
|
-u|--noup)
|
||||||
maj=0
|
maj=0
|
||||||
|
shift
|
||||||
;;
|
;;
|
||||||
"-l"|"--nolow")
|
-l|--nolow)
|
||||||
min=0
|
min=0
|
||||||
|
shift
|
||||||
;;
|
;;
|
||||||
"-e"?*|"--extracars"?*)
|
-e|--extracars)
|
||||||
local extcar=$(echo "$opt" | cut -f 2- -d '=')
|
extcar="$2"
|
||||||
|
shift 2
|
||||||
;;
|
;;
|
||||||
"-L"?*|"--length"?*)
|
-L|--length)
|
||||||
local length=$(echo "$opt" | cut -f 2- -d '=')
|
length="$2"
|
||||||
if ! [[ $length =~ ^[0-9]+$ ]]; then
|
if ! [[ $length =~ ^[1-9][0-9]*$ ]]; then
|
||||||
disp E "The --length parameter requires a number."
|
disp E "The --length parameter requires a positive integer."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
shift 2
|
||||||
;;
|
;;
|
||||||
"-o"?*|"--occurences"?*)
|
-o|--occurences|--occurrences)
|
||||||
local occurs=$(echo "$opt" | cut -f 2- -d '=')
|
occurs="$2"
|
||||||
if ! [[ $occurs =~ ^[1-9]+$ ]]; then
|
if ! [[ $occurs =~ ^[1-9][0-9]*$ ]]; then
|
||||||
disp E "The --occurs parameter requires a number from 1 to 9."
|
disp E "The --occurences parameter requires a positive integer."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
shift 2
|
||||||
;;
|
;;
|
||||||
"-*")
|
--)
|
||||||
disp E "Unknow parameter ${opt}."
|
shift
|
||||||
return 1
|
break
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
if ! [[ $opt =~ ^[1-9]+$ ]]; then
|
disp E "Invalid options, use \"genpwd --help\" to display usage."
|
||||||
disp E "Unknow parameter ${opt}."
|
|
||||||
return 1
|
return 1
|
||||||
else
|
|
||||||
local nbpwd=$opt
|
|
||||||
fi
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# Function selecting a random caracter from the list in parameter
|
if [[ $# -gt 1 ]]; then
|
||||||
pickcar()
|
disp E "Too many positional arguments. Use only [nb_passwd]."
|
||||||
{
|
|
||||||
# When a character is picked we check if it's not appearing already twice
|
|
||||||
# elsewhere, we choose an other char, to compensate weak bash randomizer
|
|
||||||
while [[ -z $char ]]; do
|
|
||||||
local char=$(echo ${1:RANDOM%${#1}:1} $RANDOM)
|
|
||||||
if [[ $(awk -F"$char" '{print NF-1}' <<< "$picked") -gt $occurs ]]; then
|
|
||||||
unset char
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
picked+="$char"
|
|
||||||
echo "$char"
|
|
||||||
}
|
|
||||||
|
|
||||||
disp I "Generating $nbpwd passwords, please wait..."
|
|
||||||
for n in $( seq 1 $nbpwd ); do
|
|
||||||
{
|
|
||||||
local carset='' # store final caracter set to use
|
|
||||||
local picked='' # store already used caracter
|
|
||||||
local rlength=0 # store already assigned length of caracters
|
|
||||||
|
|
||||||
# ?, *, $ and \ impossible to use to my knowledge as it would be interpreted
|
|
||||||
if [[ $symb == 1 ]]; then
|
|
||||||
pickcar '!.@#&%/^-_'
|
|
||||||
carset+='!.@#&%/^-_'
|
|
||||||
(( rlength++ ))
|
|
||||||
fi
|
|
||||||
if [[ $numb == 1 ]]; then
|
|
||||||
pickcar '0123456789'
|
|
||||||
carset+='0123456789'
|
|
||||||
(( rlength++ ))
|
|
||||||
fi
|
|
||||||
if [[ $maj == 1 ]]; then
|
|
||||||
pickcar 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
||||||
carset+='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
|
||||||
(( rlength++ ))
|
|
||||||
fi
|
|
||||||
if [[ $min == 1 ]]; then
|
|
||||||
pickcar 'abcdefghijklmnopqrstuvwxyz'
|
|
||||||
carset+='abcdefghijklmnopqrstuvwxyz'
|
|
||||||
(( rlength++ ))
|
|
||||||
fi
|
|
||||||
if [[ -n $extcar ]]; then
|
|
||||||
pickcar "$extcar"
|
|
||||||
carset+=$extcar
|
|
||||||
(( rlength++ ))
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if we have enough car to have something viable
|
|
||||||
if [[ ${#carset} -lt $length ]]; then
|
|
||||||
disp E 'Not enought caracters are authorised for the password length.'
|
|
||||||
disp E 'Please allow more caracter (preferably) or reduce password lentgh.'
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
for i in $( seq 1 $(( $length - $rlength )) ); do
|
if [[ $# -eq 1 ]]; then
|
||||||
pickcar "$carset"
|
nbpwd="$1"
|
||||||
|
if ! [[ $nbpwd =~ ^[1-9][0-9]*$ ]]; then
|
||||||
|
disp E "The number of passwords to generate must be a positive integer."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
local carset=""
|
||||||
|
local unique_carset=""
|
||||||
|
local ch=""
|
||||||
|
local i=0
|
||||||
|
local n=0
|
||||||
|
local idx=0
|
||||||
|
local attempts=0
|
||||||
|
local count=0
|
||||||
|
local max_attempts=0
|
||||||
|
local set=""
|
||||||
|
local char=""
|
||||||
|
local -a required_sets=()
|
||||||
|
declare -A seen_chars=()
|
||||||
|
|
||||||
|
(( symb )) && {
|
||||||
|
required_sets+=('!.@#&%/^-_')
|
||||||
|
carset+='!.@#&%/^-_'
|
||||||
|
}
|
||||||
|
(( numb )) && {
|
||||||
|
required_sets+=('0123456789')
|
||||||
|
carset+='0123456789'
|
||||||
|
}
|
||||||
|
(( maj )) && {
|
||||||
|
required_sets+=('ABCDEFGHIJKLMNOPQRSTUVWXYZ')
|
||||||
|
carset+='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||||
|
}
|
||||||
|
(( min )) && {
|
||||||
|
required_sets+=('abcdefghijklmnopqrstuvwxyz')
|
||||||
|
carset+='abcdefghijklmnopqrstuvwxyz'
|
||||||
|
}
|
||||||
|
if [[ -n $extcar ]]; then
|
||||||
|
required_sets+=("$extcar")
|
||||||
|
carset+="$extcar"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z $carset ]]; then
|
||||||
|
disp E "No characters are available. Re-enable at least one character class."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
for (( i=0; i<${#carset}; i++ )); do
|
||||||
|
ch=${carset:i:1}
|
||||||
|
if [[ -z ${seen_chars["$ch"]+x} ]]; then
|
||||||
|
seen_chars["$ch"]=1
|
||||||
|
unique_carset+="$ch"
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
} | sort -R | awk '{printf "%s", $1}'
|
unset seen_chars
|
||||||
unset picked carset rlength
|
carset="$unique_carset"
|
||||||
echo
|
|
||||||
|
if (( ${#required_sets[@]} > length )); then
|
||||||
|
disp E "The selected character classes require a longer password."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( length > ${#carset} * occurs )); then
|
||||||
|
disp E "The occurrence limit is too strict for the selected length."
|
||||||
|
disp E "Please allow more characters or increase --occurences."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
disp I "Generating $nbpwd password(s), please wait..."
|
||||||
|
for (( n=1; n<=nbpwd; n++ )); do
|
||||||
|
local -a password_chars=()
|
||||||
|
local -A char_count=()
|
||||||
|
max_attempts=$(( ${#carset} * (occurs + 1) + 32 ))
|
||||||
|
|
||||||
|
for set in "${required_sets[@]}"; do
|
||||||
|
attempts=0
|
||||||
|
while :; do
|
||||||
|
if (( attempts >= max_attempts )); then
|
||||||
|
disp E "Unable to satisfy the occurrence limit with the current settings."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
idx=$(( RANDOM % ${#set} ))
|
||||||
|
char=${set:idx:1}
|
||||||
|
count=${char_count["$char"]:-0}
|
||||||
|
|
||||||
|
if (( count < occurs )); then
|
||||||
|
char_count["$char"]=$(( count + 1 ))
|
||||||
|
password_chars+=("$char")
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
((attempts++))
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
while (( ${#password_chars[@]} < length )); do
|
||||||
|
attempts=0
|
||||||
|
while :; do
|
||||||
|
if (( attempts >= max_attempts )); then
|
||||||
|
disp E "Unable to satisfy the occurrence limit with the current settings."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
idx=$(( RANDOM % ${#carset} ))
|
||||||
|
char=${carset:idx:1}
|
||||||
|
count=${char_count["$char"]:-0}
|
||||||
|
|
||||||
|
if (( count < occurs )); then
|
||||||
|
char_count["$char"]=$(( count + 1 ))
|
||||||
|
password_chars+=("$char")
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
((attempts++))
|
||||||
|
done
|
||||||
|
done
|
||||||
|
|
||||||
|
for (( i=${#password_chars[@]} - 1; i>0; i-- )); do
|
||||||
|
idx=$(( RANDOM % (i + 1) ))
|
||||||
|
char=${password_chars[i]}
|
||||||
|
password_chars[i]=${password_chars[idx]}
|
||||||
|
password_chars[idx]=$char
|
||||||
|
done
|
||||||
|
|
||||||
|
printf '%s' "${password_chars[@]}"
|
||||||
|
printf '\n'
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
export -f genpwd
|
export -f genpwd
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# pwdscore : score a password quality from 1 to 100
|
||||||
|
# Usage: pwdscore [options] <password>
|
||||||
|
pwdscore()
|
||||||
|
{
|
||||||
|
local verbose=${PWDSCORE_DEFAULT_VERBOSE:-0}
|
||||||
|
local read_stdin=0
|
||||||
|
local password=""
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "pwdscore: Score a password from 1 to 100.\n\n"
|
||||||
|
printf "Usage: pwdscore [options] <password>\n"
|
||||||
|
printf " pwdscore [options] --stdin\n"
|
||||||
|
printf " pwdscore [options] # prompt on terminal\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||||
|
printf "\t-v, --verbose\t\tShow details about the computed score\n"
|
||||||
|
printf "\t-i, --stdin\t\tRead the password from standard input\n\n"
|
||||||
|
printf "Note:\n"
|
||||||
|
printf " Passwords containing '!' should be quoted, or passed via --stdin.\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-v|--verbose)
|
||||||
|
verbose=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-i|--stdin)
|
||||||
|
read_stdin=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
-*)
|
||||||
|
disp E "Invalid option '$1'. Use \"pwdscore --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( read_stdin )); then
|
||||||
|
[[ $# -eq 0 ]] || {
|
||||||
|
disp E "Do not pass a positional password when using --stdin."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
IFS= read -r password || true
|
||||||
|
elif [[ $# -eq 0 ]]; then
|
||||||
|
if [[ -t 0 ]]; then
|
||||||
|
read -r -s -p 'Password: ' password < /dev/tty || true
|
||||||
|
printf '\n' > /dev/tty
|
||||||
|
else
|
||||||
|
IFS= read -r password || true
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
[[ $# -eq 1 ]] || {
|
||||||
|
disp E "Please provide exactly one password to score."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
password="$1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local lower=${password,,}
|
||||||
|
local length=${#password}
|
||||||
|
local score=0
|
||||||
|
local rating="very weak"
|
||||||
|
local unique_count=0
|
||||||
|
local i=0 idx=0
|
||||||
|
local c1=0 c2=0 c3=0
|
||||||
|
local ch=""
|
||||||
|
local has_lower=0 has_upper=0 has_digit=0 has_symbol=0
|
||||||
|
local pool_size=0
|
||||||
|
local entropy_bits="0.0"
|
||||||
|
local entropy_score=0
|
||||||
|
local -A seen=()
|
||||||
|
|
||||||
|
if [[ -z $password ]]; then
|
||||||
|
printf '1\n'
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( length >= 20 )); then
|
||||||
|
score=40
|
||||||
|
elif (( length >= 16 )); then
|
||||||
|
score=34
|
||||||
|
elif (( length >= 12 )); then
|
||||||
|
score=28
|
||||||
|
elif (( length >= 8 )); then
|
||||||
|
score=18
|
||||||
|
else
|
||||||
|
score=$(( length * 2 ))
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $password =~ [a-z] ]]; then
|
||||||
|
has_lower=1
|
||||||
|
pool_size=$(( pool_size + 26 ))
|
||||||
|
score=$(( score + 12 ))
|
||||||
|
fi
|
||||||
|
if [[ $password =~ [A-Z] ]]; then
|
||||||
|
has_upper=1
|
||||||
|
pool_size=$(( pool_size + 26 ))
|
||||||
|
score=$(( score + 12 ))
|
||||||
|
fi
|
||||||
|
if [[ $password =~ [0-9] ]]; then
|
||||||
|
has_digit=1
|
||||||
|
pool_size=$(( pool_size + 10 ))
|
||||||
|
score=$(( score + 12 ))
|
||||||
|
fi
|
||||||
|
if [[ $password =~ [^[:alnum:]] ]]; then
|
||||||
|
has_symbol=1
|
||||||
|
pool_size=$(( pool_size + 33 ))
|
||||||
|
score=$(( score + 14 ))
|
||||||
|
fi
|
||||||
|
|
||||||
|
for (( i=0; i<length; i++ )); do
|
||||||
|
ch=${password:i:1}
|
||||||
|
if [[ -z ${seen["$ch"]+x} ]]; then
|
||||||
|
seen["$ch"]=1
|
||||||
|
unique_count=$(( unique_count + 1 ))
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
score=$(( score + (unique_count * 10) / length ))
|
||||||
|
|
||||||
|
if (( pool_size > 1 )); then
|
||||||
|
entropy_bits=$(awk -v len="$length" -v pool="$pool_size" \
|
||||||
|
'BEGIN { printf "%.1f", len * (log(pool) / log(2)) }')
|
||||||
|
|
||||||
|
entropy_score=$(awk -v bits="$entropy_bits" 'BEGIN {
|
||||||
|
if (bits < 28) print -35;
|
||||||
|
else if (bits < 36) print -25;
|
||||||
|
else if (bits < 60) print -10;
|
||||||
|
else if (bits < 80) print 0;
|
||||||
|
else if (bits < 100) print 5;
|
||||||
|
else print 10;
|
||||||
|
}')
|
||||||
|
score=$(( score + entropy_score ))
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ $lower =~ (password|admin|root|qwerty|azerty|welcome|letmein|secret|changeme) ]]; then
|
||||||
|
score=$(( score - 25 ))
|
||||||
|
fi
|
||||||
|
if [[ $lower =~ (1234|abcd|qwer|0000|1111|aaaa) ]]; then
|
||||||
|
score=$(( score - 15 ))
|
||||||
|
fi
|
||||||
|
if [[ $password =~ (.)\1\1 ]]; then
|
||||||
|
score=$(( score - 10 ))
|
||||||
|
fi
|
||||||
|
if (( length < 8 )); then
|
||||||
|
score=$(( score - 10 ))
|
||||||
|
fi
|
||||||
|
if (( unique_count * 2 < length )); then
|
||||||
|
score=$(( score - 10 ))
|
||||||
|
fi
|
||||||
|
|
||||||
|
for (( idx=0; idx<length-2; idx++ )); do
|
||||||
|
printf -v c1 '%d' "'${lower:idx:1}"
|
||||||
|
printf -v c2 '%d' "'${lower:idx+1:1}"
|
||||||
|
printf -v c3 '%d' "'${lower:idx+2:1}"
|
||||||
|
if (( (c2 == c1 + 1 && c3 == c2 + 1) || \
|
||||||
|
(c2 == c1 - 1 && c3 == c2 - 1) )); then
|
||||||
|
score=$(( score - 10 ))
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( score < 1 )); then
|
||||||
|
score=1
|
||||||
|
elif (( score > 100 )); then
|
||||||
|
score=100
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( score >= 90 )); then
|
||||||
|
rating='excellent'
|
||||||
|
elif (( score >= 75 )); then
|
||||||
|
rating='strong'
|
||||||
|
elif (( score >= 60 )); then
|
||||||
|
rating='good'
|
||||||
|
elif (( score >= 40 )); then
|
||||||
|
rating='fair'
|
||||||
|
elif (( score >= 20 )); then
|
||||||
|
rating='weak'
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( verbose )); then
|
||||||
|
printf 'Score: %d/100\n' "$score"
|
||||||
|
printf 'Rating: %s\n' "$rating"
|
||||||
|
printf 'Length: %d\n' "$length"
|
||||||
|
printf 'Lowercase: %s\n' "$([[ $has_lower -eq 1 ]] && echo yes || echo no)"
|
||||||
|
printf 'Uppercase: %s\n' "$([[ $has_upper -eq 1 ]] && echo yes || echo no)"
|
||||||
|
printf 'Digits: %s\n' "$([[ $has_digit -eq 1 ]] && echo yes || echo no)"
|
||||||
|
printf 'Symbols: %s\n' "$([[ $has_symbol -eq 1 ]] && echo yes || echo no)"
|
||||||
|
printf 'Unique chars: %d\n' "$unique_count"
|
||||||
|
printf 'Entropy: ~%s bits\n' "$entropy_bits"
|
||||||
|
printf 'Entropy modifier: %+d\n' "$entropy_score"
|
||||||
|
else
|
||||||
|
printf '%d\n' "$score"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
export -f pwdscore
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
load_conf "pwd"
|
||||||
|
|
||||||
|
# EOF
|
||||||
|
|||||||
@@ -1,119 +1,440 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Let the rain fall
|
# Copyright (c) 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
|
# Protected by the BSD3 license. Please read bellow for details.
|
||||||
|
#
|
||||||
|
# * Redistribution and use in source and binary forms,
|
||||||
|
# * with or without modification, are permitted provided
|
||||||
|
# * that the following conditions are met:
|
||||||
|
# *
|
||||||
|
# * Redistributions of source code must retain the above
|
||||||
|
# * copyright notice, this list of conditions and the
|
||||||
|
# * following disclaimer.
|
||||||
|
# *
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# * copyright notice, this list of conditions and the following
|
||||||
|
# * disclaimer in the documentation and/or other materials
|
||||||
|
# * provided with the distribution.
|
||||||
|
# *
|
||||||
|
# * Neither the name of the copyright holder nor the names
|
||||||
|
# * of any other contributors may be used to endorse or
|
||||||
|
# * promote products derived from this software without
|
||||||
|
# * specific prior written permission.
|
||||||
|
# *
|
||||||
|
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
# * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
# * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
# * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# * OF SUCH DAMAGE.
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
rain()
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Generic rain-like engine and presets
|
||||||
|
|
||||||
|
_rain_build_colors()
|
||||||
{
|
{
|
||||||
local exit_st=0
|
local base_color="$1"
|
||||||
local rain_cars=("|" "│" "┃" "┆" "┇" "┊" "┋" "╽" "╿")
|
RAIN_ENGINE_COLORS=()
|
||||||
local rain_colors=("\e[37m" "\e[37;1m")
|
|
||||||
# More from 256 color mode
|
|
||||||
for i in {244..255}; do
|
|
||||||
rain_colors=( "${rain_colors[@]}" "\e[38;5;${i}m" )
|
|
||||||
done
|
|
||||||
local rain_tab=${#rain_cars[@]}
|
|
||||||
local rain_color_tab=${#rain_colors[@]}
|
|
||||||
local num_rain_metadata=5
|
|
||||||
local term_height=$(tput lines)
|
|
||||||
local term_width=$(tput cols)
|
|
||||||
local step_duration=0.050
|
|
||||||
local X=0 Y=0 drop_length=0 rain_drop=0
|
|
||||||
local max_rain_width=0 new_rain_odd=0 falling_odd=0
|
|
||||||
|
|
||||||
|
case $base_color in
|
||||||
|
green)
|
||||||
|
for i in {22..28} {34..40} {46..48}; do RAIN_ENGINE_COLORS+=("\e[38;5;${i}m"); done ;;
|
||||||
|
blue)
|
||||||
|
for i in {17..21} {27..33} {39..45}; do RAIN_ENGINE_COLORS+=("\e[38;5;${i}m"); done ;;
|
||||||
|
red)
|
||||||
|
for i in {52..52} {88..88} {124..124} {160..160} {196..201}; do RAIN_ENGINE_COLORS+=("\e[38;5;${i}m"); done ;;
|
||||||
|
yellow)
|
||||||
|
for i in {58..58} {100..100} {142..142} {184..184} {226..229}; do RAIN_ENGINE_COLORS+=("\e[38;5;${i}m"); done ;;
|
||||||
|
cyan)
|
||||||
|
for i in {30..31} {37..38} {44..45} {50..51}; do RAIN_ENGINE_COLORS+=("\e[38;5;${i}m"); done ;;
|
||||||
|
*)
|
||||||
|
RAIN_ENGINE_COLORS=("\e[37m" "\e[37;1m")
|
||||||
|
for i in {244..255}; do RAIN_ENGINE_COLORS+=("\e[38;5;${i}m"); done ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
sigwinch() {
|
_rain_build_chars()
|
||||||
term_width=$(tput cols)
|
{
|
||||||
term_height=$(tput lines)
|
local mode="$1"
|
||||||
#step_duration=0.025
|
local charset="$2"
|
||||||
(( max_rain_width = term_width * term_height / 4 ))
|
RAIN_ENGINE_CHARS=()
|
||||||
(( max_rain_height = term_height < 10 ? 1 : term_height / 10 ))
|
|
||||||
# In percentage
|
case "$mode" in
|
||||||
(( new_rain_odd = term_height > 50 ? 100 : term_height * 2 ))
|
matrix)
|
||||||
(( new_rain_odd = new_rain_odd * 75 / 100 ))
|
case "$charset" in
|
||||||
(( falling_odd = term_height > 25 ? 100 : term_height * 4 ))
|
""|binary)
|
||||||
(( falling_odd = falling_odd * 90 / 100 ))
|
RAIN_ENGINE_CHARS=("0" "1")
|
||||||
|
;;
|
||||||
|
kana|kanji)
|
||||||
|
# Half-width katakana set, generally rendered as single-cell glyphs.
|
||||||
|
RAIN_ENGINE_CHARS=("ア" "イ" "ウ" "エ" "オ" "カ" "キ" "ク" "ケ" "コ" "サ" "シ" "ス" "セ" "ソ" "タ" "チ" "ツ" "テ" "ト" "ナ" "ニ" "ヌ" "ネ" "ノ" "ハ" "ヒ" "フ" "ヘ" "ホ" "マ" "ミ" "ム" "メ" "モ" "ヤ" "ユ" "ヨ" "ラ" "リ" "ル" "レ" "ロ" "ワ" "ン")
|
||||||
|
;;
|
||||||
|
ascii)
|
||||||
|
RAIN_ENGINE_CHARS=("0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "A" "B" "C" "D" "E" "F")
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Unknown charset: ${charset} (supported: binary, kana, ascii)."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
RAIN_ENGINE_CHARS=("|" "│" "┃" "┆" "┇" "┊" "┋" "╽" "╿")
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
_rain_normalize_speed()
|
||||||
|
{
|
||||||
|
local raw_speed="$1"
|
||||||
|
|
||||||
|
# Accept integer/floating values. UI scale is centiseconds by default:
|
||||||
|
# 5 -> 0.05s, 2.5 -> 0.025s. Values < 1 are treated as direct seconds
|
||||||
|
# for backward compatibility (e.g. 0.03).
|
||||||
|
if [[ ! "$raw_speed" =~ ^[0-9]+([.][0-9]+)?$ ]]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if awk -v s="$raw_speed" 'BEGIN { exit !(s < 1) }'; then
|
||||||
|
printf "%s" "$raw_speed"
|
||||||
|
else
|
||||||
|
awk -v s="$raw_speed" 'BEGIN { printf "%.3f", s / 100 }'
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
_rain_engine()
|
||||||
|
{
|
||||||
|
local step_duration="$1"
|
||||||
|
local base_color="$2"
|
||||||
|
local mode="$3"
|
||||||
|
local charset="$4"
|
||||||
|
|
||||||
|
command -v tput >/dev/null 2>&1 || {
|
||||||
|
disp E "The program 'tput' is required but not installed."
|
||||||
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
do_exit() {
|
_rain_build_colors "$base_color"
|
||||||
|
_rain_build_chars "$mode" "$charset" || return 1
|
||||||
|
|
||||||
|
local rain_colors=("${RAIN_ENGINE_COLORS[@]}")
|
||||||
|
local rain_chars=("${RAIN_ENGINE_CHARS[@]}")
|
||||||
|
local rain_color_tab=${#rain_colors[@]}
|
||||||
|
local rain_tab=${#rain_chars[@]}
|
||||||
|
local matrix_head_color=$'\e[1;97m'
|
||||||
|
|
||||||
|
local exit_st=0
|
||||||
|
local num_rain_metadata=5
|
||||||
|
local term_height=0 term_width=0
|
||||||
|
local X=0 Y=0 drop_length=0 rain_drop=0
|
||||||
|
local max_rain_width=0 max_rain_height=0
|
||||||
|
local new_rain_odd=0 falling_odd=0
|
||||||
|
local term_area=0
|
||||||
|
local frame_sleep="$step_duration"
|
||||||
|
|
||||||
|
sigwinch()
|
||||||
|
{
|
||||||
|
term_width=$(tput cols)
|
||||||
|
term_height=$(tput lines)
|
||||||
|
((term_area = term_width * term_height))
|
||||||
|
|
||||||
|
case "$mode" in
|
||||||
|
matrix)
|
||||||
|
((max_rain_width = term_area / 3))
|
||||||
|
((max_rain_height = term_height < 8 ? 1 : term_height / 6))
|
||||||
|
((new_rain_odd = term_height > 50 ? 100 : term_height * 2))
|
||||||
|
((new_rain_odd = new_rain_odd * 85 / 100))
|
||||||
|
((falling_odd = 100))
|
||||||
|
|
||||||
|
# Adapt cadence and density to terminal size for smoother rendering.
|
||||||
|
if ((term_area < 1200)); then
|
||||||
|
((max_rain_width = term_area / 4))
|
||||||
|
frame_sleep=$(awk -v s="$step_duration" 'BEGIN { printf "%.3f", s * 1.15 }')
|
||||||
|
elif ((term_area > 5000)); then
|
||||||
|
((max_rain_width = term_area / 2))
|
||||||
|
frame_sleep=$(awk -v s="$step_duration" 'BEGIN { printf "%.3f", s * 0.85 }')
|
||||||
|
else
|
||||||
|
frame_sleep="$step_duration"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
((max_rain_width = term_area / 4))
|
||||||
|
((max_rain_height = term_height < 10 ? 1 : term_height / 10))
|
||||||
|
((new_rain_odd = term_height > 50 ? 100 : term_height * 2))
|
||||||
|
((new_rain_odd = new_rain_odd * 75 / 100))
|
||||||
|
((falling_odd = term_height > 25 ? 100 : term_height * 4))
|
||||||
|
((falling_odd = falling_odd * 90 / 100))
|
||||||
|
frame_sleep="$step_duration"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
do_exit()
|
||||||
|
{
|
||||||
exit_st=1
|
exit_st=1
|
||||||
}
|
}
|
||||||
|
|
||||||
do_render() {
|
do_render()
|
||||||
# Clean screen first
|
{
|
||||||
local idx=0
|
local idx=0 y=0 drop_color="" current_char="" render_color=""
|
||||||
|
|
||||||
for ((idx = 0; idx < num_rains * num_rain_metadata; idx += num_rain_metadata)); do
|
for ((idx = 0; idx < num_rains * num_rain_metadata; idx += num_rain_metadata)); do
|
||||||
X=${rains[idx]}
|
X=${rains[idx]}
|
||||||
Y=${rains[idx + 1]}
|
Y=${rains[idx + 1]}
|
||||||
drop_length=${rains[idx + 4]}
|
drop_length=${rains[idx + 4]}
|
||||||
for ((y = Y; y < Y + drop_length; y++)); do
|
for ((y = Y; y < Y + drop_length; y++)); do
|
||||||
(( y < 1 || y > term_height )) && continue
|
((y < 1 || y > term_height)) && continue
|
||||||
echo -ne "\e[${y};${X}H "
|
printf "\e[%d;%dH " "$y" "$X"
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
|
|
||||||
for ((idx = 0; idx < num_rains * num_rain_metadata; idx += num_rain_metadata)); do
|
for ((idx = 0; idx < num_rains * num_rain_metadata; idx += num_rain_metadata)); do
|
||||||
if (( 100 * RANDOM / 32768 < falling_odd )); then
|
if ((100 * RANDOM / 32768 < falling_odd)); then
|
||||||
# Falling
|
if ((++rains[idx + 1] > term_height)); then
|
||||||
if (( ++rains[idx + 1] > term_height )); then
|
rains=("${rains[@]:0:idx}" "${rains[@]:idx+num_rain_metadata:num_rains*num_rain_metadata}")
|
||||||
# Out of screen, bye sweet <3
|
((num_rains--))
|
||||||
rains=("${rains[@]:0:idx}"
|
|
||||||
"${rains[@]:idx+num_rain_metadata:num_rains*num_rain_metadata}")
|
|
||||||
(( num_rains-- ))
|
|
||||||
continue
|
continue
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
X=${rains[idx]}
|
X=${rains[idx]}
|
||||||
Y=${rains[idx + 1]}
|
Y=${rains[idx + 1]}
|
||||||
rain_drop=${rains[idx + 2]}
|
rain_drop=${rains[idx + 2]}
|
||||||
drop_color=${rains[idx + 3]}
|
drop_color=${rains[idx + 3]}
|
||||||
drop_length=${rains[idx + 4]}
|
drop_length=${rains[idx + 4]}
|
||||||
|
|
||||||
for ((y = Y; y < Y + drop_length; y++)); do
|
for ((y = Y; y < Y + drop_length; y++)); do
|
||||||
(( y < 1 || y > term_height )) && continue
|
((y < 1 || y > term_height)) && continue
|
||||||
echo -ne "\e[${y};${X}H${drop_color}${rain_drop}"
|
if [[ "$mode" == "matrix" ]]; then
|
||||||
|
current_char="${rain_chars[rain_tab * RANDOM / 32768]}"
|
||||||
|
if ((y == Y + drop_length - 1)); then
|
||||||
|
render_color="$matrix_head_color"
|
||||||
|
else
|
||||||
|
render_color="$drop_color"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
current_char="$rain_drop"
|
||||||
|
render_color="$drop_color"
|
||||||
|
fi
|
||||||
|
printf "\e[%d;%dH%b%s" "$y" "$X" "$render_color" "$current_char"
|
||||||
done
|
done
|
||||||
done
|
done
|
||||||
}
|
}
|
||||||
|
|
||||||
trap do_exit TERM INT
|
trap do_exit TERM INT
|
||||||
trap sigwinch WINCH
|
trap sigwinch WINCH
|
||||||
# No echo stdin and hide the cursor
|
|
||||||
stty -echo
|
stty -echo
|
||||||
echo -ne "\e[?25l"
|
printf "\e[?25l"
|
||||||
|
printf "\e[2J"
|
||||||
|
|
||||||
echo -ne "\e[2J"
|
|
||||||
local rains=()
|
local rains=()
|
||||||
local num_rains=0
|
local num_rains=0
|
||||||
|
local ch=""
|
||||||
|
|
||||||
sigwinch
|
sigwinch
|
||||||
while (( exit_st <= 0 )); do
|
while ((exit_st <= 0)); do
|
||||||
if (( $exit_st <=0 )); then
|
read -r -n 1 -t "$frame_sleep" ch
|
||||||
read -n 1 -t $step_duration ch
|
|
||||||
case "$ch" in
|
case "$ch" in
|
||||||
q|Q)
|
q|Q)
|
||||||
do_exit
|
do_exit
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
if (( num_rains < max_rain_width )) && (( 100 * RANDOM / 32768 < new_rain_odd )); then
|
if ((num_rains < max_rain_width)) && ((100 * RANDOM / 32768 < new_rain_odd)); then
|
||||||
# Need new |, 1-based
|
rain_drop="${rain_chars[rain_tab * RANDOM / 32768]}"
|
||||||
rain_drop="${rain_cars[rain_tab * RANDOM / 32768]}"
|
|
||||||
drop_color="${rain_colors[rain_color_tab * RANDOM / 32768]}"
|
drop_color="${rain_colors[rain_color_tab * RANDOM / 32768]}"
|
||||||
drop_length=$(( max_rain_height * RANDOM / 32768 + 1 ))
|
drop_length=$((max_rain_height * RANDOM / 32768 + 1))
|
||||||
X=$(( term_width * RANDOM / 32768 + 1 ))
|
X=$((term_width * RANDOM / 32768 + 1))
|
||||||
Y=$(( 1 - drop_length ))
|
Y=$((1 - drop_length))
|
||||||
rains=( "${rains[@]}" "$X" "$Y" "$rain_drop" "$drop_color" "$drop_length" )
|
rains=("${rains[@]}" "$X" "$Y" "$rain_drop" "$drop_color" "$drop_length")
|
||||||
(( num_rains++ ))
|
((num_rains++))
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Let rain fall!
|
|
||||||
do_render
|
do_render
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
echo -ne "\e[${term_height};1H\e[0K"
|
|
||||||
|
|
||||||
# Show cursor and echo stdin
|
printf "\e[%d;1H\e[0K" "$term_height"
|
||||||
echo -ne "\e[?25h"
|
printf "\e[?25h"
|
||||||
stty echo
|
stty echo
|
||||||
unset exit_st
|
|
||||||
trap - TERM INT
|
trap - TERM INT
|
||||||
trap - WINCH
|
trap - WINCH
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Let the rain fall (current style)
|
||||||
|
# Usage: rain [OPTIONS]
|
||||||
|
rain()
|
||||||
|
{
|
||||||
|
_rain_show_usage()
|
||||||
|
{
|
||||||
|
printf "Usage: rain [OPTIONS]\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-s, --speed NUM Set speed value (default: 5 => 0.050s).\n"
|
||||||
|
printf "\t Values >=1 use a /100 scale (5 => 0.05s).\n"
|
||||||
|
printf "\t Values <1 are interpreted as raw seconds.\n"
|
||||||
|
printf "\t-c, --color COLOR Set the color theme (default: white).\n"
|
||||||
|
printf "\t-h, --help Display this help message and exit.\n\n"
|
||||||
|
printf "Available Colors:\n"
|
||||||
|
printf "\t\e[32mgreen\e[0m\t: Matrix-like green shades\n"
|
||||||
|
printf "\t\e[34mblue\e[0m\t: Deep ocean blue gradients\n"
|
||||||
|
printf "\t\e[31mred\e[0m\t: Crimson/Blood rain\n"
|
||||||
|
printf "\t\e[33myellow\e[0m\t: Amber and gold tones\n"
|
||||||
|
printf "\t\e[36mcyan\e[0m\t: Electric cyan/turquoise\n"
|
||||||
|
printf "\twhite\t: Greyscale and white (original style)\n\n"
|
||||||
|
printf "Example: rain --color green --speed 3\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
local _raw_speed="${RAIN_DEFAULT_SPEED:-5}"
|
||||||
|
local step_duration
|
||||||
|
step_duration=$(_rain_normalize_speed "$_raw_speed") || step_duration=0.050
|
||||||
|
local base_color="${RAIN_DEFAULT_COLOR:-white}"
|
||||||
|
|
||||||
|
while [[ "$#" -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-s|--speed)
|
||||||
|
if [[ -n "$2" && ! "$2" =~ ^- ]]; then
|
||||||
|
step_duration=$(_rain_normalize_speed "$2") || {
|
||||||
|
disp E "--speed requires a numeric value."
|
||||||
|
_rain_show_usage
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
disp E "--speed requires a numeric value."
|
||||||
|
_rain_show_usage
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
-c|--color)
|
||||||
|
if [[ -n "$2" && ! "$2" =~ ^- ]]; then
|
||||||
|
base_color="$2"
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
disp E "--color requires a color name."
|
||||||
|
_rain_show_usage
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
_rain_show_usage
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Unknown option: $1"
|
||||||
|
_rain_show_usage
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
_rain_engine "$step_duration" "$base_color" "rain" ""
|
||||||
|
}
|
||||||
export -f rain
|
export -f rain
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Matrix style digital rain
|
||||||
|
# Usage: matrix [OPTIONS]
|
||||||
|
matrix()
|
||||||
|
{
|
||||||
|
_matrix_show_usage()
|
||||||
|
{
|
||||||
|
printf "Usage: matrix [OPTIONS]\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-s, --speed NUM Set speed value (default: 3.5 => 0.035s).\n"
|
||||||
|
printf "\t Values >=1 use a /100 scale (3.5 => 0.035s).\n"
|
||||||
|
printf "\t Values <1 are interpreted as raw seconds.\n"
|
||||||
|
printf "\t-c, --color COLOR Set color theme (default: green).\n"
|
||||||
|
printf "\t-C, --charset SET Character set: binary, kana, ascii (default: binary).\n"
|
||||||
|
printf "\t-h, --help Display this help message and exit.\n\n"
|
||||||
|
printf "Example: matrix -C kana -c green --speed 2\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
local _raw_speed="${MATRIX_DEFAULT_SPEED:-3.5}"
|
||||||
|
local step_duration
|
||||||
|
step_duration=$(_rain_normalize_speed "$_raw_speed") || step_duration=0.035
|
||||||
|
local base_color="${MATRIX_DEFAULT_COLOR:-green}"
|
||||||
|
local charset="${MATRIX_DEFAULT_CHARSET:-binary}"
|
||||||
|
|
||||||
|
while [[ "$#" -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-s|--speed)
|
||||||
|
if [[ -n "$2" && ! "$2" =~ ^- ]]; then
|
||||||
|
step_duration=$(_rain_normalize_speed "$2") || {
|
||||||
|
disp E "--speed requires a numeric value."
|
||||||
|
_matrix_show_usage
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
disp E "--speed requires a numeric value."
|
||||||
|
_matrix_show_usage
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
-c|--color)
|
||||||
|
if [[ -n "$2" && ! "$2" =~ ^- ]]; then
|
||||||
|
case "${2,,}" in
|
||||||
|
binary|kana|kanji|ascii)
|
||||||
|
disp W "'${2}' looks like a charset value. Use -C/--charset for clarity."
|
||||||
|
charset="${2,,}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
base_color="$2"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
disp E "--color requires a color name."
|
||||||
|
_matrix_show_usage
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
-C|--charset)
|
||||||
|
if [[ -n "$2" && ! "$2" =~ ^- ]]; then
|
||||||
|
charset="${2,,}"
|
||||||
|
shift
|
||||||
|
else
|
||||||
|
disp E "--charset requires a value."
|
||||||
|
_matrix_show_usage
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
_matrix_show_usage
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Unknown option: $1"
|
||||||
|
_matrix_show_usage
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
shift
|
||||||
|
done
|
||||||
|
|
||||||
|
_rain_engine "$step_duration" "$base_color" "matrix" "$charset"
|
||||||
|
}
|
||||||
|
export -f matrix
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
load_conf "rain"
|
||||||
|
|
||||||
|
# EOF
|
||||||
|
|||||||
247
profile.d/ssh.sh
247
profile.d/ssh.sh
@@ -1,70 +1,207 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Remove host from know_host (name and IP) for the active user
|
# Copyright (c) 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
|
# Protected by the BSD3 license. Please read bellow for details.
|
||||||
|
#
|
||||||
|
# * Redistribution and use in source and binary forms,
|
||||||
|
# * with or without modification, are permitted provided
|
||||||
|
# * that the following conditions are met:
|
||||||
|
# *
|
||||||
|
# * Redistributions of source code must retain the above
|
||||||
|
# * copyright notice, this list of conditions and the
|
||||||
|
# * following disclaimer.
|
||||||
|
# *
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# * copyright notice, this list of conditions and the following
|
||||||
|
# * disclaimer in the documentation and/or other materials
|
||||||
|
# * provided with the distribution.
|
||||||
|
# *
|
||||||
|
# * Neither the name of the copyright holder nor the names
|
||||||
|
# * of any other contributors may be used to endorse or
|
||||||
|
# * promote products derived from this software without
|
||||||
|
# * specific prior written permission.
|
||||||
|
# *
|
||||||
|
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
# * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
# * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
# * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# * OF SUCH DAMAGE.
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
rmhost ()
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Remove host entries (name and IP) from ~/.ssh/known_hosts for the active user
|
||||||
|
# Usage: rmhost <hostname|ip> [hostname2|ip2 [...]]
|
||||||
|
rmhost()
|
||||||
{
|
{
|
||||||
if [[ "$#" -lt 1 ]]; then
|
local PARSED
|
||||||
disp E "Incorrect number of parameters."
|
local all_users=0
|
||||||
disp E "Usage: rmhost <hostname|ip> [hostname2|ip2 [...]]"
|
local -a known_hosts_files=()
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
while [[ $1 ]]; do
|
PARSED=$(getopt -o ha --long help,all-users -n 'rmhost' -- "$@")
|
||||||
local hst=$1 && shift
|
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||||
isipv4 $hst > /dev/null
|
if [[ $? -ne 0 ]]; then return 1; fi
|
||||||
local v4=$?
|
eval set -- "$PARSED"
|
||||||
isipv6 $hst > /dev/null
|
|
||||||
local v6=$?
|
|
||||||
|
|
||||||
if [[ $v4 -eq 0 || $v6 -eq 0 ]]; then
|
while true; do
|
||||||
local ip=$hst
|
case "$1" in
|
||||||
unset hst
|
-h|--help)
|
||||||
fi
|
printf "rmhost: Remove host/IP from known_hosts files.\n\n"
|
||||||
unset v4 v6
|
printf "Usage: rmhost [--all-users] <hostname|ip> [hostname2|ip2 ...]\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
if [[ ! $ip && $hst ]]; then
|
printf " -a, --all-users Remove entries from all local users when run as root\n"
|
||||||
ip=$(host $hst | grep "has address" | awk '{print $NF}')
|
printf " -h, --help Display this help screen\n"
|
||||||
[[ ! $? ]] &&
|
|
||||||
disp E "Impossible to extract IP from hostname." &&
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ $hst ]]; then
|
|
||||||
disp I "Removing host $hst from ssh known_host..."
|
|
||||||
ssh-keygen -R $hst > /dev/null
|
|
||||||
fi
|
|
||||||
if [[ $ip ]]; then
|
|
||||||
disp I "Removing IP $ip from ssh known_host..."
|
|
||||||
ssh-keygen -R $ip > /dev/null
|
|
||||||
fi
|
|
||||||
unset hst ip
|
|
||||||
done
|
|
||||||
}
|
|
||||||
export -f rmhost
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Login root via SSH on the given machine
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
ssr ()
|
|
||||||
{
|
|
||||||
for opt in $@ ; do
|
|
||||||
case $opt in
|
|
||||||
"-h"|"--help")
|
|
||||||
echo "ssr: do a root user ssh login."
|
|
||||||
echo
|
|
||||||
echo "Usage: ssr <server [ssh options]>"
|
|
||||||
return 0
|
return 0
|
||||||
;;
|
;;
|
||||||
|
-a|--all-users)
|
||||||
|
all_users=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid options, use \"rmhost --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
[[ ! $1 ]] &&
|
[[ $# -eq 0 ]] && {
|
||||||
disp E "Please specify the server you want to log in." &&
|
disp E "Missing argument. Use 'rmhost --help' for usage."
|
||||||
return 1
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
local srv=$1 && shift
|
command -v ssh-keygen >/dev/null 2>&1 || {
|
||||||
|
disp E "ssh-keygen is not installed."
|
||||||
|
return 127
|
||||||
|
}
|
||||||
|
|
||||||
ssh -Y root@$srv $@
|
if (( all_users )); then
|
||||||
|
[[ ${EUID:-$(id -u)} -eq 0 ]] || {
|
||||||
|
disp E "Option --all-users is only available when run as root."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
while IFS=: read -r _ _ _ _ _ home _; do
|
||||||
|
[[ -n $home && -f $home/.ssh/known_hosts ]] || continue
|
||||||
|
known_hosts_files+=("$home/.ssh/known_hosts")
|
||||||
|
done < /etc/passwd
|
||||||
|
|
||||||
|
[[ -f /etc/ssh/ssh_known_hosts ]] && \
|
||||||
|
known_hosts_files+=("/etc/ssh/ssh_known_hosts")
|
||||||
|
|
||||||
|
[[ ${#known_hosts_files[@]} -gt 0 ]] || {
|
||||||
|
disp W "No known_hosts files found for local users."
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
else
|
||||||
|
known_hosts_files=("${HOME}/.ssh/known_hosts")
|
||||||
|
fi
|
||||||
|
|
||||||
|
for target in "$@"; do
|
||||||
|
local hst="$target"
|
||||||
|
local ip=""
|
||||||
|
local v4=1
|
||||||
|
local v6=1
|
||||||
|
|
||||||
|
isipv4 "$hst" >/dev/null 2>&1; v4=$?
|
||||||
|
isipv6 "$hst" >/dev/null 2>&1; v6=$?
|
||||||
|
|
||||||
|
if [[ $v4 -eq 0 || $v6 -eq 0 ]]; then
|
||||||
|
ip="$hst"
|
||||||
|
hst=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -z ${ip:-} && -n ${hst:-} ]]; then
|
||||||
|
if command -v host >/dev/null 2>&1; then
|
||||||
|
ip=$(host "$hst" 2>/dev/null |
|
||||||
|
awk '/has address|has IPv6 address/ {print $NF; exit}')
|
||||||
|
elif command -v getent >/dev/null 2>&1; then
|
||||||
|
ip=$(getent ahosts "$hst" 2>/dev/null | awk 'NR == 1 {print $1; exit}')
|
||||||
|
else
|
||||||
|
disp W "No resolver tool found; removing hostname only for '$hst'."
|
||||||
|
fi
|
||||||
|
|
||||||
|
[[ -z ${ip:-} ]] && \
|
||||||
|
disp W "Could not resolve IP for '$hst'; removing hostname only."
|
||||||
|
fi
|
||||||
|
|
||||||
|
local known_hosts_file=""
|
||||||
|
for known_hosts_file in "${known_hosts_files[@]}"; do
|
||||||
|
if [[ -n ${hst:-} ]]; then
|
||||||
|
disp I "Removing host $hst from $known_hosts_file..."
|
||||||
|
if ! ssh-keygen -R "$hst" -f "$known_hosts_file" >/dev/null 2>&1; then
|
||||||
|
disp W "No known_hosts entry found for '$hst' in '$known_hosts_file'."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [[ -n ${ip:-} ]]; then
|
||||||
|
disp I "Removing IP $ip from $known_hosts_file..."
|
||||||
|
if ! ssh-keygen -R "$ip" -f "$known_hosts_file" >/dev/null 2>&1; then
|
||||||
|
disp W "No known_hosts entry found for '$ip' in '$known_hosts_file'."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
done
|
||||||
|
}
|
||||||
|
export -f rmhost
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Login root via SSH on the given machine
|
||||||
|
# Usage: ssr <server [ssh options]>
|
||||||
|
ssr()
|
||||||
|
{
|
||||||
|
case "${1:-}" in
|
||||||
|
-h|--help)
|
||||||
|
printf "ssr: SSH into a server as root.\n\n"
|
||||||
|
printf "Usage: ssr <server> [ssh_options...]\n\n"
|
||||||
|
printf "Notes:\n"
|
||||||
|
printf " The first argument is the target server.\n"
|
||||||
|
printf " All remaining arguments are passed directly to ssh.\n\n"
|
||||||
|
printf "Examples:\n"
|
||||||
|
printf " ssr srv01\n"
|
||||||
|
printf " ssr srv01 -p 2222\n"
|
||||||
|
printf " ssr srv01 -i ~/.ssh/id_ed25519 -J bastion\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
command -v ssh >/dev/null 2>&1 || {
|
||||||
|
disp E "ssh is not installed."
|
||||||
|
return 127
|
||||||
|
}
|
||||||
|
|
||||||
|
[[ $# -eq 0 || -z ${1:-} ]] && {
|
||||||
|
disp E "Please specify the server you want to log in."
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
local srv=$1
|
||||||
|
shift
|
||||||
|
|
||||||
|
# Build default options array from config, falling back to -Y (X11 forwarding)
|
||||||
|
local -a ssh_default_opts=()
|
||||||
|
if [[ -n "${SSH_DEFAULT_OPT:-}" ]]; then
|
||||||
|
read -ra ssh_default_opts <<< "${SSH_DEFAULT_OPT}"
|
||||||
|
else
|
||||||
|
ssh_default_opts=(-Y)
|
||||||
|
fi
|
||||||
|
|
||||||
|
# shellcheck disable=SC2029
|
||||||
|
ssh "${ssh_default_opts[@]}" root@"$srv" "$@"
|
||||||
}
|
}
|
||||||
export -f ssr
|
export -f ssr
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
load_conf "ssh"
|
||||||
|
|
||||||
|
# EOF
|
||||||
|
|||||||
42
profile.d/themes/abyss.theme
Normal file
42
profile.d/themes/abyss.theme
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Abyss prompt theme — deep ocean navy, electric teal, golden accents
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||||
|
# line by line. Only the following value forms are accepted:
|
||||||
|
#
|
||||||
|
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||||
|
# KEY="${ColorVarName}" — same with braces
|
||||||
|
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||||
|
#
|
||||||
|
# Accepted keys:
|
||||||
|
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||||
|
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||||
|
# from disp.sh for the whole shell session)
|
||||||
|
#
|
||||||
|
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||||
|
# warning — theme files cannot execute code.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Inspired by the VS Code "Abyss" theme: deep navy/black backdrop, electric
|
||||||
|
# teal highlights, golden-yellow accents, cool electric blue for identifiers.
|
||||||
|
# Palette overrides shift the cooler hues to their high-intensity (electric)
|
||||||
|
# equivalents — blue becomes IBlue, green shifts toward teal (ICyan).
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Palette overrides — shift colours toward cool electric hues
|
||||||
|
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="\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="\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
|
||||||
|
PROMPT_COLOR_DIR_FG="$ICyan" # teal path
|
||||||
42
profile.d/themes/adwaita.theme
Normal file
42
profile.d/themes/adwaita.theme
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Adwaita prompt theme — clean GNOME defaults, blue accent, green/red status
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||||
|
# line by line. Only the following value forms are accepted:
|
||||||
|
#
|
||||||
|
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||||
|
# KEY="${ColorVarName}" — same with braces
|
||||||
|
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||||
|
#
|
||||||
|
# Accepted keys:
|
||||||
|
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||||
|
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||||
|
# from disp.sh for the whole shell session)
|
||||||
|
#
|
||||||
|
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||||
|
# warning — theme files cannot execute code.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Follows the GNOME HIG colour intent: a single calm blue accent (#3584e4),
|
||||||
|
# pleasant green for success (#33d17a), clear red for errors (#e01b24).
|
||||||
|
# No intense saturation, no heavy remapping — legibility over spectacle.
|
||||||
|
# Palette overrides gently shift Blue and Green to their IBlue/IGreen
|
||||||
|
# variants to better match Adwaita's brighter, flatter tones.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Palette overrides — align to Adwaita's brighter accent tones
|
||||||
|
Blue="\e[0;94m" # IBlue → closer to Adwaita blue (#3584e4)
|
||||||
|
Green="\e[0;92m" # IGreen → closer to Adwaita green (#33d17a)
|
||||||
|
|
||||||
|
PROMPT_COLOR_TIME_FG="$Cyan" # calm cyan clock text
|
||||||
|
PROMPT_COLOR_TIME_BG="$On_IBlack" # dark grey clock background
|
||||||
|
PROMPT_COLOR_BAR_BG="$On_Blue" # blue main bar (Adwaita accent)
|
||||||
|
|
||||||
|
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="$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="$BBlue" # darker bold blue — readable on blue bar
|
||||||
|
PROMPT_COLOR_DIR_FG="$IGreen" # Adwaita green for path
|
||||||
32
profile.d/themes/dark.theme
Normal file
32
profile.d/themes/dark.theme
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Dark prompt theme — dark grey bar, cyan user, magenta path
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||||
|
# line by line. Only the following value forms are accepted:
|
||||||
|
#
|
||||||
|
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||||
|
# KEY="${ColorVarName}" — same with braces
|
||||||
|
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||||
|
#
|
||||||
|
# Accepted keys:
|
||||||
|
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||||
|
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||||
|
# from disp.sh for the whole shell session)
|
||||||
|
#
|
||||||
|
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||||
|
# warning — theme files cannot execute code.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
PROMPT_COLOR_TIME_FG="$ICyan" # Clock text
|
||||||
|
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
|
||||||
|
|
||||||
|
PROMPT_COLOR_ERR_BG="$On_Red" # Exit-code background on failure
|
||||||
|
PROMPT_COLOR_ERR_FG="$BIWhite" # Exit-code text on failure
|
||||||
|
PROMPT_COLOR_ERR_MARK="$BIYellow" # X mark colour on failure
|
||||||
|
|
||||||
|
PROMPT_COLOR_ROOT_FG="$BIRed" # Username colour when root
|
||||||
|
PROMPT_COLOR_USER_FG="$ICyan" # Username@host colour for normal users
|
||||||
|
PROMPT_COLOR_DIR_FG="$IPurple" # Working directory colour
|
||||||
32
profile.d/themes/default.theme
Normal file
32
profile.d/themes/default.theme
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# Default prompt theme — blue bar, green user, cyan path
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||||
|
# line by line. Only the following value forms are accepted:
|
||||||
|
#
|
||||||
|
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||||
|
# KEY="${ColorVarName}" — same with braces
|
||||||
|
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||||
|
#
|
||||||
|
# Accepted keys:
|
||||||
|
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||||
|
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||||
|
# from disp.sh for the whole shell session)
|
||||||
|
#
|
||||||
|
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||||
|
# warning — theme files cannot execute code.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
PROMPT_COLOR_TIME_FG="$Blue" # Clock text
|
||||||
|
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
|
||||||
|
PROMPT_COLOR_OK_MARK="$Green" # Checkmark colour on success
|
||||||
|
|
||||||
|
PROMPT_COLOR_ERR_BG="$On_Red" # Exit-code background on failure
|
||||||
|
PROMPT_COLOR_ERR_FG="$White" # Exit-code text on failure
|
||||||
|
PROMPT_COLOR_ERR_MARK="$BYellow" # X mark colour on failure
|
||||||
|
|
||||||
|
PROMPT_COLOR_ROOT_FG="$Red" # Username colour when root
|
||||||
|
PROMPT_COLOR_USER_FG="$Green" # Username@host colour for normal users
|
||||||
|
PROMPT_COLOR_DIR_FG="$ICyan" # Working directory colour
|
||||||
35
profile.d/themes/light.theme
Normal file
35
profile.d/themes/light.theme
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# Light prompt theme — white bar, blue user, purple path
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||||
|
# line by line. Only the following value forms are accepted:
|
||||||
|
#
|
||||||
|
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||||
|
# KEY="${ColorVarName}" — same with braces
|
||||||
|
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||||
|
#
|
||||||
|
# Accepted keys:
|
||||||
|
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||||
|
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||||
|
# from disp.sh for the whole shell session)
|
||||||
|
#
|
||||||
|
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||||
|
# warning — theme files cannot execute code.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Opposite of dark.theme: backgrounds flip to bright whites, foregrounds
|
||||||
|
# shift to their dark/regular equivalents for contrast on a light terminal.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
PROMPT_COLOR_OK_FG="$Green" # Exit-code text on success (IGreen → Green)
|
||||||
|
PROMPT_COLOR_OK_MARK="$BGreen" # Checkmark colour on success (unchanged — bold green reads on both)
|
||||||
|
|
||||||
|
PROMPT_COLOR_ERR_BG="$On_Red" # Exit-code background on failure (unchanged)
|
||||||
|
PROMPT_COLOR_ERR_FG="$BIWhite" # Exit-code text on failure (unchanged — white on red works on both)
|
||||||
|
PROMPT_COLOR_ERR_MARK="$BYellow" # X mark on failure (BIYellow → BYellow, less glaring on light)
|
||||||
|
|
||||||
|
PROMPT_COLOR_ROOT_FG="$Red" # Username when root (BIRed → Red)
|
||||||
|
PROMPT_COLOR_USER_FG="$Blue" # Username@host normal user (ICyan → Blue)
|
||||||
|
PROMPT_COLOR_DIR_FG="$Purple" # Working directory (IPurple → Purple)
|
||||||
64
profile.d/themes/monochrome.theme
Normal file
64
profile.d/themes/monochrome.theme
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# Monochrome prompt theme — strict greyscale, no hue at all
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||||
|
# line by line. Only the following value forms are accepted:
|
||||||
|
#
|
||||||
|
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||||
|
# KEY="${ColorVarName}" — same with braces
|
||||||
|
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||||
|
#
|
||||||
|
# Accepted keys:
|
||||||
|
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||||
|
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||||
|
# from disp.sh for the whole shell session)
|
||||||
|
#
|
||||||
|
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||||
|
# warning — theme files cannot execute code.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# All hues are silenced — colour variables are remapped to greyscale ANSI
|
||||||
|
# codes so that ls, man, grep, etc. also lose their colour cues. Contrast
|
||||||
|
# is achieved entirely through brightness: dark grey / light grey / white.
|
||||||
|
# Error state uses an inverted (white background, black text) bar so it
|
||||||
|
# remains visually distinct without relying on red.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Palette overrides — replace every hue with a grey equivalent
|
||||||
|
Red="\e[0;37m" # light grey
|
||||||
|
Green="\e[0;97m" # bright white (success intent kept as brightest)
|
||||||
|
Yellow="\e[0;90m" # dark grey
|
||||||
|
Blue="\e[0;90m" # dark grey
|
||||||
|
Purple="\e[0;37m" # light grey
|
||||||
|
Cyan="\e[0;37m" # light grey
|
||||||
|
BRed="\e[1;37m" # bold light grey
|
||||||
|
BGreen="\e[1;97m" # bold bright white
|
||||||
|
BYellow="\e[1;90m" # bold dark grey
|
||||||
|
BBlue="\e[1;90m" # bold dark grey
|
||||||
|
BPurple="\e[1;37m" # bold light grey
|
||||||
|
BCyan="\e[1;37m" # bold light grey
|
||||||
|
IRed="\e[0;97m" # bright white
|
||||||
|
IGreen="\e[0;97m" # bright white
|
||||||
|
IYellow="\e[0;90m" # dark grey
|
||||||
|
IBlue="\e[0;90m" # dark grey
|
||||||
|
IPurple="\e[0;37m" # light grey
|
||||||
|
ICyan="\e[0;37m" # light grey
|
||||||
|
BIRed="\e[1;97m" # bold bright white
|
||||||
|
BIGreen="\e[1;97m" # bold bright white
|
||||||
|
BIYellow="\e[1;90m" # bold dark grey
|
||||||
|
BIBlue="\e[1;90m" # bold dark grey
|
||||||
|
BIPurple="\e[1;37m" # bold light grey
|
||||||
|
BICyan="\e[1;37m" # bold light grey
|
||||||
|
|
||||||
|
PROMPT_COLOR_TIME_FG="$IBlack" # dark grey clock text (subtle)
|
||||||
|
PROMPT_COLOR_TIME_BG="$On_IBlack" # dark grey clock background
|
||||||
|
PROMPT_COLOR_BAR_BG="$On_IBlack" # dark grey main bar
|
||||||
|
|
||||||
|
PROMPT_COLOR_OK_FG="$IWhite" # bright white on success
|
||||||
|
PROMPT_COLOR_OK_MARK="$BIWhite" # bold bright white checkmark
|
||||||
|
|
||||||
|
PROMPT_COLOR_ERR_BG="$On_White" # inverted: bright white bar on error
|
||||||
|
PROMPT_COLOR_ERR_FG="$Black" # black text on white background
|
||||||
|
PROMPT_COLOR_ERR_MARK="$BBlack" # bold black X
|
||||||
|
|
||||||
|
PROMPT_COLOR_ROOT_FG="$BIWhite" # bold bright white for root warning
|
||||||
|
PROMPT_COLOR_USER_FG="$IWhite" # bright white for normal user
|
||||||
|
PROMPT_COLOR_DIR_FG="$White" # standard white for path
|
||||||
45
profile.d/themes/monokai.theme
Normal file
45
profile.d/themes/monokai.theme
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Monokai prompt theme — high-saturation, vivid hues on near-black
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||||
|
# line by line. Only the following value forms are accepted:
|
||||||
|
#
|
||||||
|
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||||
|
# KEY="${ColorVarName}" — same with braces
|
||||||
|
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||||
|
#
|
||||||
|
# Accepted keys:
|
||||||
|
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||||
|
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||||
|
# from disp.sh for the whole shell session)
|
||||||
|
#
|
||||||
|
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||||
|
# warning — theme files cannot execute code.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Monokai's signature: lime green, orange-yellow, hot pink/red, bright violet,
|
||||||
|
# electric cyan — all on a near-black (#272822) background.
|
||||||
|
# Palette overrides remap the dim ANSI regulars to their vivid high-intensity
|
||||||
|
# equivalents so that ls, grep colour output, etc. also look more "Monokai".
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Palette overrides — boost regular colours to Monokai-vivid equivalents
|
||||||
|
Red="\e[0;91m" # hot pink/red (#F92672)
|
||||||
|
Green="\e[0;92m" # lime green (#A6E22E)
|
||||||
|
Yellow="\e[0;93m" # orange-yellow (#E6DB74 / #FD971F)
|
||||||
|
Blue="\e[0;94m" # electric blue (#66D9E8 → shift blue)
|
||||||
|
Purple="\e[0;95m" # bright violet (#AE81FF)
|
||||||
|
Cyan="\e[0;96m" # electric cyan (#66D9E8)
|
||||||
|
|
||||||
|
PROMPT_COLOR_TIME_FG="$IYellow" # orange clock text
|
||||||
|
PROMPT_COLOR_TIME_BG="$On_IBlack" # near-black Monokai background
|
||||||
|
PROMPT_COLOR_BAR_BG="$On_IBlack" # flat dark bar
|
||||||
|
|
||||||
|
PROMPT_COLOR_OK_FG="$IGreen" # lime green on success
|
||||||
|
PROMPT_COLOR_OK_MARK="$IGreen" # lime green checkmark
|
||||||
|
|
||||||
|
PROMPT_COLOR_ERR_BG="$On_IBlack" # keep dark — use colour for contrast
|
||||||
|
PROMPT_COLOR_ERR_FG="$IRed" # hot pink on failure
|
||||||
|
PROMPT_COLOR_ERR_MARK="$IRed" # hot pink X
|
||||||
|
|
||||||
|
PROMPT_COLOR_ROOT_FG="$IRed" # hot pink for root
|
||||||
|
PROMPT_COLOR_USER_FG="$IYellow" # orange-yellow for user
|
||||||
|
PROMPT_COLOR_DIR_FG="$ICyan" # electric cyan for path
|
||||||
42
profile.d/themes/plasma.theme
Normal file
42
profile.d/themes/plasma.theme
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Plasma prompt theme — vivid KDE Plasma purples and electric cyan
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||||
|
# line by line. Only the following value forms are accepted:
|
||||||
|
#
|
||||||
|
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||||
|
# KEY="${ColorVarName}" — same with braces
|
||||||
|
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||||
|
#
|
||||||
|
# Accepted keys:
|
||||||
|
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||||
|
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||||
|
# from disp.sh for the whole shell session)
|
||||||
|
#
|
||||||
|
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||||
|
# warning — theme files cannot execute code.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Captures the charged, vivid energy of KDE Plasma: dominant bright magenta/
|
||||||
|
# purple, electric cyan highlights, all on a dark background. Palette overrides
|
||||||
|
# boost Blue, Purple, and Cyan to their high-intensity equivalents so that
|
||||||
|
# shell colour output reflects the same vivid palette.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Palette overrides — vivid charged hues
|
||||||
|
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="$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
|
||||||
|
|
||||||
|
PROMPT_COLOR_ERR_BG="$On_Red" # red bar on failure
|
||||||
|
PROMPT_COLOR_ERR_FG="$IWhite" # bright white text
|
||||||
|
PROMPT_COLOR_ERR_MARK="$IYellow" # yellow X
|
||||||
|
|
||||||
|
PROMPT_COLOR_ROOT_FG="$IRed" # red for root
|
||||||
|
PROMPT_COLOR_USER_FG="$BIPurple" # bold vivid purple for user
|
||||||
|
PROMPT_COLOR_DIR_FG="$ICyan" # electric cyan path
|
||||||
127
profile.d/themes/solarized-light.theme
Normal file
127
profile.d/themes/solarized-light.theme
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
# Solarized Light prompt theme — exact 24-bit / true-colour palette
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||||
|
# line by line. Only the following value forms are accepted:
|
||||||
|
#
|
||||||
|
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||||
|
# KEY="${ColorVarName}" — same with braces
|
||||||
|
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||||
|
#
|
||||||
|
# Accepted keys:
|
||||||
|
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||||
|
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||||
|
# from disp.sh for the whole shell session)
|
||||||
|
#
|
||||||
|
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||||
|
# warning — theme files cannot execute code.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Requires a terminal with true-colour / 24-bit support.
|
||||||
|
# Check with: printf '\e[38;2;220;50;47mred\e[0m\n'
|
||||||
|
# If you see solid red text, your terminal supports this theme.
|
||||||
|
# (konsole, iTerm2, kitty, alacritty, Windows Terminal all do)
|
||||||
|
#
|
||||||
|
# Solarized Light base tones (inverted vs Dark):
|
||||||
|
# Base3 #fdf6e3 →253 246 227 (main background — lightest)
|
||||||
|
# Base2 #eee8d5 →238 232 213 (background highlights)
|
||||||
|
# Base1 #93a1a1 →147 161 161 (comments / secondary content)
|
||||||
|
# Base0 #839496 →131 148 150 (body text — secondary)
|
||||||
|
# Base00 #657b83 →101 123 131 (body text — main on light bg)
|
||||||
|
# Base01 #586e75 → 88 110 117 (emphasis)
|
||||||
|
# Base02 #073642 → 7 54 66 (darkest — used for strong contrast)
|
||||||
|
#
|
||||||
|
# Accent colours are identical to Solarized Dark:
|
||||||
|
# Yellow #b58900 →181 137 0
|
||||||
|
# Orange #cb4b16 →203 75 22
|
||||||
|
# Red #dc322f →220 50 47
|
||||||
|
# Magenta #d33682 →211 54 130
|
||||||
|
# Violet #6c71c4 →108 113 196
|
||||||
|
# Blue #268bd2 → 38 139 210
|
||||||
|
# Cyan #2aa198 → 42 161 152
|
||||||
|
# Green #859900 →133 153 0
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ---- Foreground palette overrides -------------------------------------------
|
||||||
|
# The accent fg colours are identical to Dark — only the base/neutral roles flip.
|
||||||
|
# Regular
|
||||||
|
Black="\e[38;2;253;246;227m" # Base3 — lightest (fg on light bg = invisible; used as bg fg pair complement)
|
||||||
|
Red="\e[38;2;220;50;47m" # Red
|
||||||
|
Green="\e[38;2;133;153;0m" # Green
|
||||||
|
Yellow="\e[38;2;181;137;0m" # Yellow — primary accent
|
||||||
|
Blue="\e[38;2;38;139;210m" # Blue
|
||||||
|
Purple="\e[38;2;211;54;130m" # Magenta
|
||||||
|
Cyan="\e[38;2;42;161;152m" # Cyan
|
||||||
|
White="\e[38;2;101;123;131m" # Base00 — main body text on light bg
|
||||||
|
|
||||||
|
# Bold
|
||||||
|
BBlack="\e[1;38;2;238;232;213m" # Base2 bold
|
||||||
|
BRed="\e[1;38;2;220;50;47m" # Red bold
|
||||||
|
BGreen="\e[1;38;2;133;153;0m" # Green bold
|
||||||
|
BYellow="\e[1;38;2;181;137;0m" # Yellow bold
|
||||||
|
BBlue="\e[1;38;2;38;139;210m" # Blue bold
|
||||||
|
BPurple="\e[1;38;2;211;54;130m" # Magenta bold
|
||||||
|
BCyan="\e[1;38;2;42;161;152m" # Cyan bold
|
||||||
|
BWhite="\e[1;38;2;88;110;117m" # Base01 bold — emphasis text
|
||||||
|
|
||||||
|
# High intensity (emphasis / I* family)
|
||||||
|
IBlack="\e[38;2;147;161;161m" # Base1 — secondary/comments
|
||||||
|
IRed="\e[38;2;203;75;22m" # Orange — Solarized's "bright red"
|
||||||
|
IGreen="\e[38;2;133;153;0m" # Green (no brighter variant)
|
||||||
|
IYellow="\e[38;2;181;137;0m" # Yellow (no brighter variant)
|
||||||
|
IBlue="\e[38;2;108;113;196m" # Violet — Solarized's "bright blue"
|
||||||
|
IPurple="\e[38;2;211;54;130m" # Magenta (no brighter variant)
|
||||||
|
ICyan="\e[38;2;42;161;152m" # Cyan (no brighter variant)
|
||||||
|
IWhite="\e[38;2;88;110;117m" # Base01 — emphasis
|
||||||
|
|
||||||
|
# Bold high intensity
|
||||||
|
BIBlack="\e[1;38;2;147;161;161m" # Base1 bold
|
||||||
|
BIRed="\e[1;38;2;203;75;22m" # Orange bold
|
||||||
|
BIGreen="\e[1;38;2;133;153;0m" # Green bold
|
||||||
|
BIYellow="\e[1;38;2;181;137;0m" # Yellow bold
|
||||||
|
BIBlue="\e[1;38;2;108;113;196m" # Violet bold
|
||||||
|
BIPurple="\e[1;38;2;211;54;130m" # Magenta bold
|
||||||
|
BICyan="\e[1;38;2;42;161;152m" # Cyan bold
|
||||||
|
BIWhite="\e[1;38;2;88;110;117m" # Base01 bold
|
||||||
|
|
||||||
|
# ---- Background palette overrides -------------------------------------------
|
||||||
|
# Light bases flip: On_Black → Base3 (lightest), On_IBlack → Base2 (highlights)
|
||||||
|
On_Black="\e[48;2;253;246;227m" # Base3 — main light background
|
||||||
|
On_Red="\e[48;2;220;50;47m" # Red
|
||||||
|
On_Green="\e[48;2;133;153;0m" # Green
|
||||||
|
On_Yellow="\e[48;2;181;137;0m" # Yellow
|
||||||
|
On_Blue="\e[48;2;38;139;210m" # Blue
|
||||||
|
On_Purple="\e[48;2;211;54;130m" # Magenta
|
||||||
|
On_Cyan="\e[48;2;42;161;152m" # Cyan
|
||||||
|
On_White="\e[48;2;101;123;131m" # Base00
|
||||||
|
|
||||||
|
On_IBlack="\e[48;2;238;232;213m" # Base2 — background highlights (slightly darker than Base3)
|
||||||
|
On_IRed="\e[48;2;203;75;22m" # Orange
|
||||||
|
On_IGreen="\e[48;2;133;153;0m" # Green
|
||||||
|
On_IYellow="\e[48;2;181;137;0m" # Yellow
|
||||||
|
On_IBlue="\e[48;2;108;113;196m" # Violet
|
||||||
|
On_IPurple="\e[48;2;211;54;130m" # Magenta
|
||||||
|
On_ICyan="\e[48;2;42;161;152m" # Cyan
|
||||||
|
On_IWhite="\e[48;2;88;110;117m" # Base01
|
||||||
|
|
||||||
|
# ---- Special codes ----------------------------------------------------------
|
||||||
|
DEFAULTFG="\e[38;2;101;123;131m" # Base00 — default foreground on light bg
|
||||||
|
DEFAULTBG="\e[48;2;253;246;227m" # Base3 — default background
|
||||||
|
RESETCOL="\e[0m"
|
||||||
|
|
||||||
|
# ---- Prompt colour slots ----------------------------------------------------
|
||||||
|
# All specified as direct ANSI sequences to avoid ordering dependencies
|
||||||
|
# with the palette overrides above.
|
||||||
|
|
||||||
|
PROMPT_COLOR_TIME_FG="\e[38;2;181;137;0m" # Yellow — primary accent
|
||||||
|
PROMPT_COLOR_TIME_BG="\e[48;2;238;232;213m" # Base2 — slightly darker than bg
|
||||||
|
PROMPT_COLOR_BAR_BG="\e[48;2;238;232;213m" # Base2 — warm light bar
|
||||||
|
|
||||||
|
PROMPT_COLOR_OK_FG="\e[38;2;7;54;66m" # Base02 — dark text for contrast on light
|
||||||
|
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 — light text on red
|
||||||
|
PROMPT_COLOR_ERR_MARK="\e[38;2;253;246;227m" # Base3 — X mark (bright on red)
|
||||||
|
|
||||||
|
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
|
||||||
|
PROMPT_COLOR_DIR_FG="\e[38;2;38;139;210m" # Blue — working directory
|
||||||
122
profile.d/themes/solarized.theme
Normal file
122
profile.d/themes/solarized.theme
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
# Solarized Dark prompt theme — exact 24-bit / true-colour palette
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||||
|
# line by line. Only the following value forms are accepted:
|
||||||
|
#
|
||||||
|
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||||
|
# KEY="${ColorVarName}" — same with braces
|
||||||
|
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||||
|
#
|
||||||
|
# Accepted keys:
|
||||||
|
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||||
|
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||||
|
# from disp.sh for the whole shell session)
|
||||||
|
#
|
||||||
|
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||||
|
# warning — theme files cannot execute code.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Requires a terminal with true-colour / 24-bit support.
|
||||||
|
# Check with: printf '\e[38;2;220;50;47mred\e[0m\n'
|
||||||
|
# If you see solid red text, your terminal supports this theme.
|
||||||
|
# (konsole, iTerm2, kitty, alacritty, Windows Terminal all do)
|
||||||
|
#
|
||||||
|
# Solarized Dark exact hex → RGB mapping used below:
|
||||||
|
# Base03 #002b36 → 0 43 54 (darkest background)
|
||||||
|
# Base02 #073642 → 7 54 66 (background highlights)
|
||||||
|
# Base01 #586e75 → 88 110 117 (comments / secondary content)
|
||||||
|
# Base00 #657b83 →101 123 131 (body text — dark background)
|
||||||
|
# Base0 #839496 →131 148 150 (body text — main)
|
||||||
|
# Base1 #93a1a1 →147 161 161 (optional emphasis)
|
||||||
|
# Yellow #b58900 →181 137 0
|
||||||
|
# Orange #cb4b16 →203 75 22
|
||||||
|
# Red #dc322f →220 50 47
|
||||||
|
# Magenta #d33682 →211 54 130
|
||||||
|
# Violet #6c71c4 →108 113 196
|
||||||
|
# Blue #268bd2 → 38 139 210
|
||||||
|
# Cyan #2aa198 → 42 161 152
|
||||||
|
# Green #859900 →133 153 0
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# ---- Foreground palette overrides -------------------------------------------
|
||||||
|
# Regular
|
||||||
|
Black="\e[38;2;0;43;54m" # Base03 — darkest content fg
|
||||||
|
Red="\e[38;2;220;50;47m" # Red
|
||||||
|
Green="\e[38;2;133;153;0m" # Green
|
||||||
|
Yellow="\e[38;2;181;137;0m" # Yellow — primary accent
|
||||||
|
Blue="\e[38;2;38;139;210m" # Blue
|
||||||
|
Purple="\e[38;2;211;54;130m" # Magenta
|
||||||
|
Cyan="\e[38;2;42;161;152m" # Cyan
|
||||||
|
White="\e[38;2;131;148;150m" # Base0 — body text
|
||||||
|
|
||||||
|
# Bold
|
||||||
|
BBlack="\e[1;38;2;7;54;66m" # Base02 bold
|
||||||
|
BRed="\e[1;38;2;220;50;47m" # Red bold
|
||||||
|
BGreen="\e[1;38;2;133;153;0m" # Green bold
|
||||||
|
BYellow="\e[1;38;2;181;137;0m" # Yellow bold
|
||||||
|
BBlue="\e[1;38;2;38;139;210m" # Blue bold
|
||||||
|
BPurple="\e[1;38;2;211;54;130m" # Magenta bold
|
||||||
|
BCyan="\e[1;38;2;42;161;152m" # Cyan bold
|
||||||
|
BWhite="\e[1;38;2;147;161;161m" # Base1 bold
|
||||||
|
|
||||||
|
# High intensity (brighter / emphasis roles in Solarized)
|
||||||
|
IBlack="\e[38;2;88;110;117m" # Base01 — secondary/comments
|
||||||
|
IRed="\e[38;2;203;75;22m" # Orange — Solarized's "bright red"
|
||||||
|
IGreen="\e[38;2;133;153;0m" # Green (no brighter variant)
|
||||||
|
IYellow="\e[38;2;181;137;0m" # Yellow (no brighter variant)
|
||||||
|
IBlue="\e[38;2;108;113;196m" # Violet — Solarized's "bright blue"
|
||||||
|
IPurple="\e[38;2;211;54;130m" # Magenta (no brighter variant)
|
||||||
|
ICyan="\e[38;2;42;161;152m" # Cyan (no brighter variant)
|
||||||
|
IWhite="\e[38;2;147;161;161m" # Base1 — optional emphasis
|
||||||
|
|
||||||
|
# Bold high intensity
|
||||||
|
BIBlack="\e[1;38;2;88;110;117m" # Base01 bold
|
||||||
|
BIRed="\e[1;38;2;203;75;22m" # Orange bold
|
||||||
|
BIGreen="\e[1;38;2;133;153;0m" # Green bold
|
||||||
|
BIYellow="\e[1;38;2;181;137;0m" # Yellow bold
|
||||||
|
BIBlue="\e[1;38;2;108;113;196m" # Violet bold
|
||||||
|
BIPurple="\e[1;38;2;211;54;130m" # Magenta bold
|
||||||
|
BICyan="\e[1;38;2;42;161;152m" # Cyan bold
|
||||||
|
BIWhite="\e[1;38;2;147;161;161m" # Base1 bold
|
||||||
|
|
||||||
|
# ---- Background palette overrides -------------------------------------------
|
||||||
|
On_Black="\e[48;2;0;43;54m" # Base03
|
||||||
|
On_Red="\e[48;2;220;50;47m" # Red
|
||||||
|
On_Green="\e[48;2;133;153;0m" # Green
|
||||||
|
On_Yellow="\e[48;2;181;137;0m" # Yellow
|
||||||
|
On_Blue="\e[48;2;38;139;210m" # Blue
|
||||||
|
On_Purple="\e[48;2;211;54;130m" # Magenta
|
||||||
|
On_Cyan="\e[48;2;42;161;152m" # Cyan
|
||||||
|
On_White="\e[48;2;131;148;150m" # Base0
|
||||||
|
|
||||||
|
On_IBlack="\e[48;2;7;54;66m" # Base02 — background highlights
|
||||||
|
On_IRed="\e[48;2;203;75;22m" # Orange
|
||||||
|
On_IGreen="\e[48;2;133;153;0m" # Green
|
||||||
|
On_IYellow="\e[48;2;181;137;0m" # Yellow
|
||||||
|
On_IBlue="\e[48;2;108;113;196m" # Violet
|
||||||
|
On_IPurple="\e[48;2;211;54;130m" # Magenta
|
||||||
|
On_ICyan="\e[48;2;42;161;152m" # Cyan
|
||||||
|
On_IWhite="\e[48;2;147;161;161m" # Base1
|
||||||
|
|
||||||
|
# ---- Special codes ----------------------------------------------------------
|
||||||
|
DEFAULTFG="\e[38;2;131;148;150m" # Base0 — default foreground
|
||||||
|
DEFAULTBG="\e[48;2;0;43;54m" # Base03 — default background
|
||||||
|
RESETCOL="\e[0m"
|
||||||
|
|
||||||
|
# ---- Prompt colour slots ----------------------------------------------------
|
||||||
|
# All specified as direct ANSI sequences to avoid ordering dependencies
|
||||||
|
# with the palette overrides above.
|
||||||
|
|
||||||
|
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;88;110;117m" # Base01 — slightly 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;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
|
||||||
|
PROMPT_COLOR_DIR_FG="\e[38;2;38;139;210m" # Blue — working directory
|
||||||
419
profile.d/updates.sh
Normal file
419
profile.d/updates.sh
Normal file
@@ -0,0 +1,419 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Copyright (c) 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
|
# Protected by the BSD3 license. Please read bellow for details.
|
||||||
|
#
|
||||||
|
# * Redistribution and use in source and binary forms,
|
||||||
|
# * with or without modification, are permitted provided
|
||||||
|
# * that the following conditions are met:
|
||||||
|
# *
|
||||||
|
# * Redistributions of source code must retain the above
|
||||||
|
# * copyright notice, this list of conditions and the
|
||||||
|
# * following disclaimer.
|
||||||
|
# *
|
||||||
|
# * Redistributions in binary form must reproduce the above
|
||||||
|
# * copyright notice, this list of conditions and the following
|
||||||
|
# * disclaimer in the documentation and/or other materials
|
||||||
|
# * provided with the distribution.
|
||||||
|
# *
|
||||||
|
# * Neither the name of the copyright holder nor the names
|
||||||
|
# * of any other contributors may be used to endorse or
|
||||||
|
# * promote products derived from this software without
|
||||||
|
# * specific prior written permission.
|
||||||
|
# *
|
||||||
|
# * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
||||||
|
# * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||||
|
# * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
# * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||||
|
# * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
# * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||||
|
# * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
# * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
# * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||||
|
# * OF SUCH DAMAGE.
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export BASE_URL="https://git.geoffray-levasseur.org/fatalerrors/profile"
|
||||||
|
|
||||||
|
# Load [updates] configuration before building the URLs so that
|
||||||
|
# UPDT_DEFAULT_BRANCH (if set in profile.conf) is already available.
|
||||||
|
load_conf "updates"
|
||||||
|
|
||||||
|
export UPDT_DEFAULT_BRANCH="${UPDT_DEFAULT_BRANCH:-master}"
|
||||||
|
export UPDT_URL="$BASE_URL/raw/branch/$UPDT_DEFAULT_BRANCH"
|
||||||
|
export ARCH_URL="$BASE_URL/archive/$UPDT_DEFAULT_BRANCH.tar.gz"
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Check whether a newer profile version is available
|
||||||
|
# Usage: check_updates [-q]
|
||||||
|
# If -q is specified, the function will operate in quiet mode (internal use only)
|
||||||
|
check_updates()
|
||||||
|
{
|
||||||
|
local quiet=0 result=5 PARSED
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "check_updates: Check whether a newer profile version is available.\n\n"
|
||||||
|
printf "Usage: check_updates [-q|--quiet]\n"
|
||||||
|
printf "This command only checks availability; it does not modify the installation.\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-q|--quiet)
|
||||||
|
quiet=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
(( quiet != 1 )) && disp I "Checking for updates..."
|
||||||
|
|
||||||
|
vfile=$(mktemp /tmp/profile_version.XXXXXX) || {
|
||||||
|
disp E "Failed to create a temporary file."
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
|
||||||
|
dwl "$UPDT_URL/version" "$vfile" >/dev/null 2>&1 || {
|
||||||
|
rm -f "$vfile"
|
||||||
|
disp E "Cannot download version file; unable to continue."
|
||||||
|
return 5
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ -s $vfile ]]; then
|
||||||
|
lastver=$(<"$vfile")
|
||||||
|
if [[ "$lastver" != "$PROFVERSION" ]]; then
|
||||||
|
disp I "Installed: $PROFVERSION. Available: $lastver."
|
||||||
|
(( quiet != 1 )) && disp I "You should upgrade when possible."
|
||||||
|
result=1
|
||||||
|
else
|
||||||
|
(( quiet != 1 )) && disp I "Your version is up-to-date."
|
||||||
|
result=0
|
||||||
|
fi
|
||||||
|
rm -f "$vfile"
|
||||||
|
else
|
||||||
|
rm -f "$vfile"
|
||||||
|
disp E "Temporary file is unreadable; unable to continue."
|
||||||
|
fi
|
||||||
|
|
||||||
|
return $result
|
||||||
|
}
|
||||||
|
export -f check_updates
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Apply the available profile upgrade
|
||||||
|
# Usage: profile_upgrade [options]
|
||||||
|
profile_upgrade()
|
||||||
|
{
|
||||||
|
local PARSED
|
||||||
|
local check_rc=0 dry_run=0 force_git=0 switch_to_git=0
|
||||||
|
local archive_file="" tmpbase="" use_archive=0 branch=""
|
||||||
|
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
|
||||||
|
fi
|
||||||
|
eval set -- "$PARSED"
|
||||||
|
while true; do
|
||||||
|
case "$1" in
|
||||||
|
-h|--help)
|
||||||
|
printf "profile_upgrade: Apply the available profile upgrade.\n\n"
|
||||||
|
printf "Usage: profile_upgrade [options]\n\n"
|
||||||
|
printf "Options:\n"
|
||||||
|
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||||
|
printf "\t-f, --file ARCHIVE\tUse a local archive file for the upgrade\n"
|
||||||
|
printf "\t-t, --tmpdir DIR\tCreate the temporary working directory under DIR\n"
|
||||||
|
printf "\t-b, --branch NAME\tUse NAME as the target Git branch\n"
|
||||||
|
printf "\t-g, --switch-to-git\tReplace current install with a fresh Git clone\n"
|
||||||
|
printf "\t-n, --dry-run\t\tDisplay what would be done without changing anything\n"
|
||||||
|
printf "\t-F, --force\t\tDiscard local changes before upgrading\n\n"
|
||||||
|
printf "If the profile is installed from Git, the upgrade uses 'git pull'.\n"
|
||||||
|
printf "Otherwise, it downloads or applies an archive and refreshes the files.\n"
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
-f|--file)
|
||||||
|
archive_file="$2"
|
||||||
|
use_archive=1
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-t|--tmpdir)
|
||||||
|
tmpbase="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-b|--branch)
|
||||||
|
branch="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-g|--switch-to-git)
|
||||||
|
switch_to_git=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-n|--dry-run)
|
||||||
|
dry_run=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-F|--force)
|
||||||
|
force_git=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--)
|
||||||
|
shift
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
disp E "Invalid options, use \"profile_upgrade --help\" to display usage."
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if (( ! use_archive && ! switch_to_git )); then
|
||||||
|
check_updates -q
|
||||||
|
check_rc=$?
|
||||||
|
if (( check_rc == 0 )); then
|
||||||
|
disp I "No update available."
|
||||||
|
return 0
|
||||||
|
elif (( check_rc > 1 )); then
|
||||||
|
disp E "Unable to check whether an update is available."
|
||||||
|
return "$check_rc"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -s $MYPATH/profile.sh ]]; then
|
||||||
|
disp E "Install path detection failed; cannot upgrade automatically."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -d $MYPATH/.git ]] && (( use_archive )) && (( ! force_git )); then
|
||||||
|
disp E "Refusing archive upgrade on a Git install without --force."
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( switch_to_git )); then
|
||||||
|
command -v git >/dev/null 2>&1 || {
|
||||||
|
disp E "Git is required to switch this install to a Git clone."
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
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"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -d $MYPATH/.git ]]; then
|
||||||
|
disp W "Git repository already present; no switch is needed."
|
||||||
|
else
|
||||||
|
local backup_dir="${MYPATH}.pre-git.$$.bak"
|
||||||
|
mv "$MYPATH" "$backup_dir" || {
|
||||||
|
disp E "Failed to move current install out of the way."
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
git clone "$BASE_URL" "$MYPATH" || {
|
||||||
|
disp E "Git clone failed; previous install kept in $backup_dir."
|
||||||
|
mv "$backup_dir" "$MYPATH" 2>/dev/null || true
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
[[ -n "$branch" ]] && (
|
||||||
|
cd "$MYPATH" && git checkout "$branch"
|
||||||
|
) || true
|
||||||
|
disp I "Switched installation to Git source."
|
||||||
|
disp I "Previous install kept in $backup_dir."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -d $MYPATH/.git ]] && (( ! use_archive )); then
|
||||||
|
disp I "Git installation detected, applying git pull."
|
||||||
|
command -v git >/dev/null 2>&1 || {
|
||||||
|
disp E "Git is required for this upgrade but is not available."
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
pushd "$MYPATH" >/dev/null || {
|
||||||
|
disp E "Failed to change directory to $MYPATH."
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
git rev-parse --is-inside-work-tree >/dev/null 2>&1 || {
|
||||||
|
disp E "Install directory is not a valid Git working tree."
|
||||||
|
popd >/dev/null || return 1
|
||||||
|
return 3
|
||||||
|
}
|
||||||
|
|
||||||
|
# Resolve the target branch: explicit -b flag > config default > master
|
||||||
|
local target_branch="${branch:-${UPDT_DEFAULT_BRANCH:-master}}"
|
||||||
|
branch="$target_branch"
|
||||||
|
|
||||||
|
# Detect current local branch and warn+switch when it differs
|
||||||
|
local current_branch
|
||||||
|
current_branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)
|
||||||
|
if [[ -n "$current_branch" && "$current_branch" != "$branch" ]]; then
|
||||||
|
disp W "Branch mismatch: currently on '$current_branch', configured branch is '$branch'."
|
||||||
|
if (( dry_run )); then
|
||||||
|
disp I "[dry-run] git fetch origin $branch"
|
||||||
|
disp I "[dry-run] git checkout $branch"
|
||||||
|
disp W "[dry-run] Branch would be changed from '$current_branch' to '$branch'."
|
||||||
|
else
|
||||||
|
git fetch origin "$branch" || {
|
||||||
|
disp E "Git fetch failed for branch $branch."
|
||||||
|
popd >/dev/null || return 1
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
git checkout "$branch" || {
|
||||||
|
disp E "Git checkout failed for branch $branch."
|
||||||
|
popd >/dev/null || return 1
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
disp W "Branch has been changed from '$current_branch' to '$branch'."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! git diff --quiet || ! git diff --cached --quiet || [[ -n $(git ls-files --others --exclude-standard) ]]; then
|
||||||
|
if (( force_git )); then
|
||||||
|
disp W "Force mode: local Git changes and untracked files will be lost."
|
||||||
|
if (( dry_run )); then
|
||||||
|
disp I "[dry-run] git fetch --all --prune"
|
||||||
|
disp I "[dry-run] git reset --hard HEAD"
|
||||||
|
disp I "[dry-run] git clean -fd"
|
||||||
|
else
|
||||||
|
git fetch --all --prune || {
|
||||||
|
disp E "Git fetch failed, upgrade not applied."
|
||||||
|
popd >/dev/null || return 1
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
git reset --hard HEAD || {
|
||||||
|
disp E "Git reset failed, upgrade not applied."
|
||||||
|
popd >/dev/null || return 1
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
git clean -fd || {
|
||||||
|
disp E "Git clean failed, upgrade not applied."
|
||||||
|
popd >/dev/null || return 1
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
disp W "The Git working tree contains local changes."
|
||||||
|
disp W "Consider committing or stashing them before upgrading, or use --force."
|
||||||
|
disp W "Upgrade may fail if the changes conflict with the upgrade."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if (( dry_run )); then
|
||||||
|
disp I "[dry-run] git pull origin $branch"
|
||||||
|
else
|
||||||
|
git pull origin "$branch" || {
|
||||||
|
disp E "Git pull failed, upgrade not applied."
|
||||||
|
popd >/dev/null || return 1
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
disp I "Successfully upgraded using git."
|
||||||
|
fi
|
||||||
|
popd >/dev/null || return 1
|
||||||
|
else
|
||||||
|
if (( use_archive )); then
|
||||||
|
[[ -r "$archive_file" ]] || {
|
||||||
|
disp E "Local archive '$archive_file' is missing or unreadable."
|
||||||
|
return 4
|
||||||
|
}
|
||||||
|
disp I "Using local archive $archive_file."
|
||||||
|
else
|
||||||
|
disp W "No Git repo found. Git is the recommended source."
|
||||||
|
disp I "Applying upgrade from archive..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ -n "$tmpbase" ]]; then
|
||||||
|
if (( dry_run )); then
|
||||||
|
disp I "[dry-run] mkdir -p \"$tmpbase\""
|
||||||
|
disp I "[dry-run] mktemp -d \"$tmpbase/profile_upg.XXXXXX\""
|
||||||
|
tmpdir="$tmpbase/profile_upg.DRYRUN"
|
||||||
|
else
|
||||||
|
mkdir -p "$tmpbase" || {
|
||||||
|
disp E "Failed to create temporary directory base $tmpbase."
|
||||||
|
return 5
|
||||||
|
}
|
||||||
|
tmpdir=$(mktemp -d "$tmpbase/profile_upg.XXXXXX") || {
|
||||||
|
disp E "Failed to create temp working directory under $tmpbase."
|
||||||
|
return 5
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
if (( dry_run )); then
|
||||||
|
disp I "[dry-run] mktemp -d /tmp/profile_upg.XXXXXX"
|
||||||
|
tmpdir="/tmp/profile_upg.DRYRUN"
|
||||||
|
else
|
||||||
|
tmpdir=$(mktemp -d /tmp/profile_upg.XXXXXX) || {
|
||||||
|
disp E "Failed to create temporary directory."
|
||||||
|
return 5
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( use_archive )); then
|
||||||
|
archive="$archive_file"
|
||||||
|
else
|
||||||
|
archive="$tmpdir/profile.tar.gz"
|
||||||
|
if (( dry_run )); then
|
||||||
|
disp I "[dry-run] dwl \"$ARCH_URL\" \"$archive\""
|
||||||
|
else
|
||||||
|
dwl "$ARCH_URL" "$archive" || {
|
||||||
|
disp E "Failed to download archive."
|
||||||
|
rm -rf "$tmpdir"
|
||||||
|
return 6
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if (( dry_run )); then
|
||||||
|
disp I "[dry-run] tar -xzf \"$archive\" -C \"$tmpdir\""
|
||||||
|
disp I "[dry-run] cp -a <extracted_profile>/. \"$MYPATH\"/"
|
||||||
|
else
|
||||||
|
tar -xzf "$archive" -C "$tmpdir" || {
|
||||||
|
disp E "Archive extraction failed."
|
||||||
|
rm -rf "$tmpdir"
|
||||||
|
return 7
|
||||||
|
}
|
||||||
|
|
||||||
|
extracted_root=$(find "$tmpdir" -mindepth 1 -maxdepth 1 -type d ! -name '.*' | head -n 1)
|
||||||
|
if [[ -z "$extracted_root" ]]; then
|
||||||
|
disp E "Could not find extracted profile files."
|
||||||
|
rm -rf "$tmpdir"
|
||||||
|
return 8
|
||||||
|
fi
|
||||||
|
|
||||||
|
disp I "Installing new version..."
|
||||||
|
cp -a "$extracted_root"/. "$MYPATH"/ || {
|
||||||
|
disp E "Failed to copy new files into $MYPATH."
|
||||||
|
rm -rf "$tmpdir"
|
||||||
|
return 9
|
||||||
|
}
|
||||||
|
|
||||||
|
disp I "Upgrade complete. Please log out and log in again."
|
||||||
|
rm -rf "$tmpdir"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
export -f profile_upgrade
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# EOF
|
||||||
324
profile.sh
324
profile.sh
@@ -1,44 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
# Begin profile
|
# Begin profile
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# Initial version from Beyond Linux From Scratch by
|
# Copyright (c) 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||||
# * James Robertson <jameswrobertson@earthlink.net>
|
|
||||||
# * Dagmar d'Surreal <rivyqntzne@pbzpnfg.arg>
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Current version from Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
|
||||||
# 16/02/2013 v1.0.0 : Initial version
|
|
||||||
# 24/10/2015 v2.0.0 : Added advanced functionnalities (clean, srr, etc.)
|
|
||||||
# 04/02/2017 v2.0.1 : clean improvements (--shell)
|
|
||||||
# 16/09/2018 v2.1.0 : Added rmhost, setc, setfr, more locales management
|
|
||||||
# 23/09/2019 v2.1.1 : [bugfix] dpkgs
|
|
||||||
# 24/09/2019 v2.1.2 : [bugfix] bug in profile version display
|
|
||||||
# 16/12/2019 v2.2.0 : Added showinfo, primary write of showdiskmap
|
|
||||||
# 08/01/2020 v2.3.0 : Added use of figlet and neofetch as a motd replace
|
|
||||||
# 16/01/2020 v2.3.1 : [bugfix] non-interactive were blocked with some functions
|
|
||||||
# 31/01/2020 v2.3.2 : Figlet: changed default font to ansi_shadow
|
|
||||||
# 02/03/2020 v2.4.0 : Added command auzip
|
|
||||||
# 03/03/2020 v2.5.0 : Added command taz and rmspc, auzip => utaz improved
|
|
||||||
# 05/03/2020 v2.5.1 : Language consistancy fix, added pigz support in taz
|
|
||||||
# 06/03/2020 v2.5.2 : Few aliases sorted out
|
|
||||||
# 11/09/2020 v2.5.3 : Few more aliases, improved code consistancy and typo,
|
|
||||||
# : improved utaz, removed showdiskmap, removed remaining French,
|
|
||||||
# : added license information for future publication
|
|
||||||
# 24/10/2020 v2.6.0 : Added session save and restore for Konsole
|
|
||||||
# 25/12/2020 v2.6.1 : Add check on rmhost, improvements rmspc, created expendlist
|
|
||||||
# 26/02/2021 v2.6.2 : [bugfix] taz: corrected bug with trailing slash on directories
|
|
||||||
# 18/10/2021 v2.6.3 : changed PS1 for status bar style version, few minor improvements
|
|
||||||
# 21/06/2022 v2.7.0 : added isipv4 and isipv6, use it in rmhost as an improvement
|
|
||||||
# 22/06/2022 v2.7.1 : [bugfix] few minor corrections, added help command
|
|
||||||
# 24/06/2022 v2.8.0 : Added backtrace, error and settrace, corrected showinfo
|
|
||||||
# 19/07/2022 v2.8.1 : few cleanup, fixes and optimizations
|
|
||||||
# 29/07/2022 v2.8.2 : added warning for non bash users
|
|
||||||
# 27/08/2022 v3.0.0 : splitted everything, added rain screensaver
|
|
||||||
# 07/11/2022 v3.0.1 : added concatenation to rmspc, added ku, error managed in meteo
|
|
||||||
# 08/11/2022 v3.1.0 : added password generator
|
|
||||||
# 10/11/2022 v3.1.1 : genpwd: test if password is doable
|
|
||||||
# 18/11/2022 v3.2.0 : created disp command for display, make use of it
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Copyright (c) 2013-2022 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
|
||||||
# Protected by the BSD3 license. Please read bellow for details.
|
# Protected by the BSD3 license. Please read bellow for details.
|
||||||
#
|
#
|
||||||
# * Redistribution and use in source and binary forms,
|
# * Redistribution and use in source and binary forms,
|
||||||
@@ -72,46 +35,166 @@
|
|||||||
# * OF SUCH DAMAGE.
|
# * OF SUCH DAMAGE.
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
export PROFVERSION="3.2.0"
|
if [[ ! $SHELL =~ bash ]]; then
|
||||||
|
echo "That environment script is designed to be used with bash being the shell."
|
||||||
export DEFAULT_CITY="Toulouse"
|
echo "Please consider using bash to enjoy our features!"
|
||||||
|
|
||||||
if [[ ! $(echo $SHELL | grep bash) ]]; then
|
|
||||||
echo "That environmet script is designed to be used with bash or zsh being the shell."
|
|
||||||
echo "Please consider using bash or zsh instead, or patch me ;)!"
|
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Required for associative arrays (4.0+) and namerefs (4.3+)
|
||||||
|
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 0 2>/dev/null) && return 1 || exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# path* : private functions for PATH variable management
|
# path* : private functions for PATH variable management
|
||||||
# ------------------------------------------------------------------------------
|
pathremove()
|
||||||
pathremove ()
|
|
||||||
{
|
{
|
||||||
local ifs=':'
|
[[ -z "$1" ]] && return 0
|
||||||
local newpath
|
local IFS=':'
|
||||||
local dir
|
local newpath dir
|
||||||
local pathvar=${2:-PATH}
|
local pathvar="${2:-PATH}"
|
||||||
for dir in ${!pathvar} ; do
|
[[ "$pathvar" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] || {
|
||||||
if [ "$dir" != "$1" ] ; then
|
printf "pathremove: unsafe variable name '%s'\n" "$pathvar" >&2
|
||||||
newpath=${newpath:+$newpath:}$dir
|
return 1
|
||||||
|
}
|
||||||
|
for dir in ${!pathvar}; do
|
||||||
|
[[ "$dir" != "$1" ]] && newpath="${newpath:+$newpath:}$dir"
|
||||||
|
done
|
||||||
|
export "$pathvar=$newpath"
|
||||||
|
}
|
||||||
|
|
||||||
|
#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()
|
||||||
|
{
|
||||||
|
[[ -z "$1" ]] && return 0
|
||||||
|
local pathvar="${2:-PATH}"
|
||||||
|
[[ "$pathvar" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] || {
|
||||||
|
printf "pathappend: unsafe variable name '%s'\n" "$pathvar" >&2
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
pathremove "$1" "$pathvar"
|
||||||
|
export "$pathvar=${!pathvar:+${!pathvar}:}$1"
|
||||||
|
}
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Configuration file parser
|
||||||
|
parse_conf()
|
||||||
|
{
|
||||||
|
local config_file="$1"
|
||||||
|
local current_section=""
|
||||||
|
local key value
|
||||||
|
|
||||||
|
[[ ! -f "$config_file" ]] && return 1
|
||||||
|
|
||||||
|
while IFS='=' read -r key value || [[ -n "$key" ]]; do
|
||||||
|
# Internal trimming (removes leading/trailing whitespace & CR)
|
||||||
|
key="${key%"${key##*[![:space:]]}"}"
|
||||||
|
key="${key#"${key%%[![:space:]]*}"}"
|
||||||
|
key="${key%$'\r'}" # Strip potential Windows line endings
|
||||||
|
|
||||||
|
# Skip comments and empty lines
|
||||||
|
[[ -z "$key" || "$key" =~ ^[#\;] ]] && continue
|
||||||
|
|
||||||
|
# Section Detection: [section_name]
|
||||||
|
if [[ "$key" =~ ^\[([a-zA-Z0-9_]+)\]$ ]]; then
|
||||||
|
current_section="${BASH_REMATCH[1]}"
|
||||||
|
declare -g -A "CONF_$current_section"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Secure Assignment (if inside a section)
|
||||||
|
if [[ -n "$current_section" ]]; then
|
||||||
|
# Clean the value
|
||||||
|
value="${value%"${value##*[![:space:]]}"}"
|
||||||
|
value="${value#"${value%%[![:space:]]*}"}"
|
||||||
|
value="${value%$'\r'}"
|
||||||
|
|
||||||
|
# Protect against command injection by disallowing certain characters in keys
|
||||||
|
value="${value//\`/}"
|
||||||
|
value="${value//\$\(/}"
|
||||||
|
|
||||||
|
# Correctly interpretet internal variables (e.g. $HOME)
|
||||||
|
if [[ "$value" == *\$* ]]; then
|
||||||
|
value=$(envsubst <<< "$value")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Strip quotes (handling both " and ')
|
||||||
|
value="${value%\"}"; value="${value#\"}"
|
||||||
|
value="${value%\'}"; value="${value#\'}"
|
||||||
|
|
||||||
|
# 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"
|
||||||
|
}
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Load command aliases from configuration
|
||||||
|
load_alias()
|
||||||
|
{
|
||||||
|
local section_name="CONF_$1"
|
||||||
|
|
||||||
|
# Check if the associative array exists using declare -p
|
||||||
|
[[ "$(declare -p "$section_name" 2>/dev/null)" != "declare -A"* ]] && return 1
|
||||||
|
|
||||||
|
# Create a nameref to the section array
|
||||||
|
local -n current_aliases="$section_name"
|
||||||
|
|
||||||
|
# Iterate safely over the keys of the associative array
|
||||||
|
for key in "${!current_aliases[@]}"; do
|
||||||
|
local cmd="${current_aliases[$key]}"
|
||||||
|
|
||||||
|
# Extract the base command (first word) safely without awk
|
||||||
|
local base_cmd="${cmd%% *}"
|
||||||
|
|
||||||
|
# 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
|
fi
|
||||||
done
|
done
|
||||||
export $pathvar="$newpath"
|
|
||||||
}
|
}
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
pathprepend ()
|
|
||||||
{
|
|
||||||
pathremove $1 $2
|
|
||||||
local pathvar=${2:-PATH}
|
|
||||||
export $pathvar="$1${!pathvar:+:${!pathvar}}"
|
|
||||||
}
|
|
||||||
|
|
||||||
pathappend ()
|
# ------------------------------------------------------------------------------
|
||||||
|
# Load configuration values as environment variables
|
||||||
|
load_conf()
|
||||||
{
|
{
|
||||||
pathremove $1 $2
|
local section_name="CONF_$1"
|
||||||
local pathvar=${2:-PATH}
|
|
||||||
export $pathvar="${!pathvar:+${!pathvar}:}$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"
|
||||||
|
|
||||||
|
for key in "${!current_vars[@]}"; do
|
||||||
|
# Export the key/value pair as a standard shell variable
|
||||||
|
# We use 'export' directly; Bash handles the assignment safely here
|
||||||
|
export "$key"="${current_vars[$key]}"
|
||||||
|
done
|
||||||
}
|
}
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
@@ -120,84 +203,81 @@ pathappend ()
|
|||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Store script's path (realpath -s resolve symlinks if profile.sh is a symlink)
|
||||||
|
# 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
|
||||||
|
MYPATH=$(dirname "$(realpath -s "${BASH_SOURCE[0]}")")
|
||||||
|
else
|
||||||
|
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 ] Unable to determine installation path, pretty much nothing will work."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ ! -s "$MYPATH/version" ]]; then
|
||||||
|
echo "[ Warning ] Unable to determine running profile version; your installation might be broken."
|
||||||
|
fi
|
||||||
|
PROFVERSION=$(cat "$MYPATH"/version)
|
||||||
|
export PROFVERSION
|
||||||
|
|
||||||
# Build PATH environment variable
|
# Build PATH environment variable
|
||||||
if [[ $EUID -eq 0 ]] ; then
|
if [[ $EUID -eq 0 ]]; then
|
||||||
pathappend /sbin:/usr/sbin
|
pathappend /sbin:/usr/sbin
|
||||||
fi
|
fi
|
||||||
[[ -d /share/services/gestparc ]] && pathappend /share/services/gestparc
|
|
||||||
[[ -d ~/bin ]] && pathappend ~/bin
|
[[ -d ~/bin ]] && pathappend ~/bin
|
||||||
[[ -d ~/.local/bin ]] && pathappend ~/.local/bin
|
[[ -d ~/.local/bin ]] && pathappend ~/.local/bin
|
||||||
|
|
||||||
# Set bash history
|
# Parse and load general configuration
|
||||||
export HISTSIZE=50000
|
export PROFILE_CONF="$MYPATH/profile.conf"
|
||||||
export HISTIGNORE="&:[bf]g:exit"
|
parse_conf "$PROFILE_CONF"
|
||||||
|
load_conf system # Load Bash system behavior configuration (history, pager, etc.)
|
||||||
|
load_conf general # General purpose configuration (compilation flags, etc.)
|
||||||
|
|
||||||
# Set default pager
|
# Load module scripts
|
||||||
export PAGER=less
|
shopt -s nullglob
|
||||||
|
for script in "$MYPATH/profile.d/"*.sh; do
|
||||||
# More colors
|
if [[ -f "$script" && -r "$script" ]]; then
|
||||||
export TERM=xterm-256color
|
# shellcheck source=/dev/null
|
||||||
|
. "$script" || printf "[ Warning ] Failed to source module: %s\n" "$script" >&2
|
||||||
# Set some compiling values
|
|
||||||
export CFLAGS="-O2 -pipe -march=native"
|
|
||||||
export MAKEFLAGS='-j12'
|
|
||||||
export PKGSOURCES='/share/src/archives'
|
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
# Default values could be altered after this line
|
|
||||||
# ------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
# Load personal configuration
|
|
||||||
[[ -f ~/.profile.conf ]] && . ~/.profile.conf
|
|
||||||
|
|
||||||
# Execute optionnal config script if any
|
|
||||||
for script in ~/profile.d/*.sh ; do
|
|
||||||
if [ -r $script ] ; then
|
|
||||||
. $script
|
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
shopt -u nullglob
|
||||||
|
|
||||||
# Interactive shell detection, two methods available each one of those might have different result
|
# Interactive shell detection, two methods available each one of those might have different result
|
||||||
# depending on distribution
|
# depending on distribution
|
||||||
#shopt -q login_shell && INTERACTIVE=1
|
#shopt -q login_shell && INTERACTIVE=1
|
||||||
[[ $- == *i* ]] && INTERACTIVE=1
|
[[ $- == *i* ]] && export INTERACTIVE=1
|
||||||
|
|
||||||
if [[ $INTERACTIVE ]]; then
|
if [[ $INTERACTIVE ]]; then
|
||||||
# For compiling (as we often compile with LFS/0linux...)
|
# For compiling (as we often compile with LFS/0linux...)
|
||||||
#Aliases
|
#Aliases
|
||||||
alias ll='ls -laFh --color=auto'
|
load_alias aliases
|
||||||
alias la='ls -Ah --color=auto'
|
|
||||||
alias l='ls -CF --color=auto'
|
|
||||||
alias ls='ls --color=auto'
|
|
||||||
|
|
||||||
alias grep='grep --color=auto'
|
|
||||||
alias egrep='egrep --color=auto'
|
|
||||||
alias fgrep='fgrep --color=auto'
|
|
||||||
alias qfind="find . -name "
|
|
||||||
|
|
||||||
alias mkck='make check'
|
|
||||||
alias mkin='make install'
|
|
||||||
alias mkdin='make DESTDIR=$PWD/dest-install install'
|
|
||||||
alias ssh='ssh -Y'
|
|
||||||
|
|
||||||
alias wget='wget -c' # resume mode by default
|
|
||||||
alias myip='curl ip.appspot.com'
|
|
||||||
|
|
||||||
# Human readable by default
|
|
||||||
alias df='df -H'
|
|
||||||
alias du='du -ch'
|
|
||||||
|
|
||||||
alias sdu='du -sk ./* | sort -n'
|
|
||||||
|
|
||||||
# Define PS1
|
# Define PS1
|
||||||
trap 'timer_start' DEBUG
|
trap 'timer_start' DEBUG
|
||||||
PROMPT_COMMAND='set_prompt'
|
PROMPT_COMMAND='set_prompt'
|
||||||
|
|
||||||
# Set default language
|
# Set default language from DEFAULT_LANG config key (set in [general]).
|
||||||
setfr
|
# The value must match one of the alias names defined in SET_LOCALE so that
|
||||||
showinfo
|
# the corresponding set<alias> function exists after build_locale_shortcuts.
|
||||||
disp I "Profile version $PROFVERSION chargé..."
|
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 loaded..."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Cleanup
|
# Cleanup
|
||||||
|
|||||||
Reference in New Issue
Block a user