Compare commits
99 Commits
v3.1.0
...
bc0a592fa1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | |||
| 5b9d7a983e | |||
| d7e6d81126 | |||
| 4a975db1c0 | |||
| c5a43a3942 |
30
LICENSE
30
LICENSE
@@ -1,11 +1,29 @@
|
||||
Copyright (c) <year> <owner>.
|
||||
Copyright 2013-2022 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.
|
||||
|
||||
335
README.md
335
README.md
@@ -1,3 +1,336 @@
|
||||
# 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 |
|
||||
| `pkgs` | packages | Search for a pattern in installed package names (dpkg/rpm, 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 `[general]` 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.
|
||||
|
||||
### 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
|
||||
|
||||
71
doc/CHANGELOG.md
Executable file
71
doc/CHANGELOG.md
Executable file
@@ -0,0 +1,71 @@
|
||||
# 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.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.
|
||||
171
doc/CONTRIBUTING.md
Executable file
171
doc/CONTRIBUTING.md
Executable file
@@ -0,0 +1,171 @@
|
||||
# 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. Development environment
|
||||
|
||||
| Requirement | Minimum version | Notes |
|
||||
|---|---|---|
|
||||
| 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
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 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.
|
||||
- **`printf`** instead of `echo` wherever the format matters.
|
||||
- **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.
|
||||
|
||||
---
|
||||
|
||||
## 5. 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`.
|
||||
|
||||
---
|
||||
|
||||
## 6. 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.
|
||||
|
||||
---
|
||||
|
||||
## 7. 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.
|
||||
|
||||
---
|
||||
|
||||
## 8. 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.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 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.
|
||||
|
||||
---
|
||||
|
||||
## 10. 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
|
||||
247
doc/profile.conf.example
Executable file
247
doc/profile.conf.example
Executable file
@@ -0,0 +1,247 @@
|
||||
# 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
|
||||
|
||||
# ==============================================================================
|
||||
[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
|
||||
121
doc/todo.md
Executable file
121
doc/todo.md
Executable file
@@ -0,0 +1,121 @@
|
||||
# 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]**
|
||||
- [ ] **`shellcheck` clean pass** — run `shellcheck -x profile.sh profile.d/*.sh`
|
||||
and address every remaining warning (currently a handful of SC2034 and SC2086
|
||||
items). Integrate as a pre-commit hook. **[easy]**
|
||||
|
||||
---
|
||||
|
||||
## 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]**
|
||||
|
||||
### packages
|
||||
- [ ] **Additional backends** — add support for `pacman` (Arch), `apk` (Alpine),
|
||||
`xbps-query` (Void), and `brew` (macOS). **[medium]**
|
||||
|
||||
### processes
|
||||
- [ ] **`ku` dry-run flag** — add `-n` / `--dry-run` to print what would be
|
||||
killed without acting. **[easy]**
|
||||
|
||||
### 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,121 +1,407 @@
|
||||
#!/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()
|
||||
{
|
||||
for opt in $@ ; do
|
||||
case $opt in
|
||||
"-h"|"--help")
|
||||
echo "utaz: uncompress all the given files and/or the ones found in the given"
|
||||
echo " directories creating an host directory where needed."
|
||||
echo
|
||||
echo "Usage: utaz [option] [directorie(s)|file(s)]"
|
||||
echo
|
||||
echo "Options:"
|
||||
echo " -h, --help Display that help screen"
|
||||
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"
|
||||
echo
|
||||
return 0
|
||||
;;
|
||||
_ununzip()
|
||||
{
|
||||
unzip -o "$1" -d "$2" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
"-d"|"--delete")
|
||||
local willrm=1
|
||||
;;
|
||||
_untar()
|
||||
{
|
||||
tar -xf "$1" -C "$2"
|
||||
}
|
||||
|
||||
"-c"|"--create-dir")
|
||||
local createdir=1
|
||||
;;
|
||||
_ungzip()
|
||||
{
|
||||
tar -xzf "$1" -C "$2"
|
||||
}
|
||||
|
||||
"-n"|"--no-dir")
|
||||
local nodir=1
|
||||
;;
|
||||
_unbzip2()
|
||||
{
|
||||
tar -xjf "$1" -C "$2"
|
||||
}
|
||||
|
||||
"-"*)
|
||||
echo "Invalid option, use \"utaz --help\" to display options list"
|
||||
echo
|
||||
return 1
|
||||
;;
|
||||
_unxz()
|
||||
{
|
||||
tar -xJf "$1" -C "$2"
|
||||
}
|
||||
|
||||
*)
|
||||
# The ${opt%/} writing is to remove trailing / if any
|
||||
local LIST="$LIST ${opt%/}"
|
||||
;;
|
||||
_unlzop()
|
||||
{
|
||||
lzop -d "$1" -o "$2/$(basename "${1%.*}")"
|
||||
}
|
||||
|
||||
_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
|
||||
}
|
||||
|
||||
_ununrar()
|
||||
{
|
||||
unrar x -o+ "$1" "$2/" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
_ununarj()
|
||||
{
|
||||
unarj e "$1" "$2/" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
_unlha()
|
||||
{
|
||||
# lha typically extracts into the current directory
|
||||
# We ensure it hits the target directory
|
||||
(cd "$2" && lha -x "../$1") >/dev/null 2>&1
|
||||
}
|
||||
|
||||
_ununace()
|
||||
{
|
||||
unace x "$1" "$2/" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
_un7z()
|
||||
{
|
||||
7z x "$1" -o"$2/" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
_unzstd()
|
||||
{
|
||||
# Zstd decompresses files directly, often requiring tar for archives
|
||||
tar --zstd -xf "$1" -C "$2"
|
||||
}
|
||||
|
||||
_uncpio()
|
||||
{
|
||||
# CPIO requires careful directory handling
|
||||
(cd "$2" && cpio -id < "../$1") >/dev/null 2>&1
|
||||
}
|
||||
|
||||
_uncabextract()
|
||||
{
|
||||
# Requires 'cabextract' package
|
||||
cabextract "$1" -d "$2/" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
_undeb()
|
||||
{
|
||||
# Extracts data content from a Debian package
|
||||
dpkg-deb -x "$1" "$2/" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
_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=$(getopt -o hdcn --long help,delete,create-dir,no-dir -n 'utaz' -- "$@")
|
||||
|
||||
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
|
||||
;;
|
||||
-d|--delete)
|
||||
local willrm=1
|
||||
shift
|
||||
;;
|
||||
-c|--create-dir)
|
||||
local createdir=1
|
||||
shift
|
||||
;;
|
||||
-n|--no-dir)
|
||||
local nodir=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid option, use \"utaz --help\" to display options list"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
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 ]] && echo "*** Error: --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
|
||||
[[ $(ls $zitem/*.zip 2> /dev/null | wc -l) -eq 0 ]] &&
|
||||
echo "$zitem contains no supported archive file, skipping." &&
|
||||
for zitem in "${FILES[@]}"; do
|
||||
# Build list of input files to process, with whitespace-safe handling.
|
||||
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
|
||||
fi
|
||||
else
|
||||
disp W "Path ${zitem} is not a file or directory, skipping."
|
||||
continue
|
||||
fi
|
||||
|
||||
for f in $zitem/*.zip; do
|
||||
echo -n "Processing archive $zitem/$f... "
|
||||
local dir=${f::-4}
|
||||
|
||||
mkdir -p $dir
|
||||
[[ $? -gt 0 ]] &&
|
||||
echo "[ filesystem can't create directories, exit ]" &&
|
||||
return 1
|
||||
|
||||
unzip -o $f -d $dir > /dev/null 2>&1
|
||||
case $? in
|
||||
0)
|
||||
[[ $willrm ]] && rm -f $f && echo -n "Deleted ! "
|
||||
for f in "${targets[@]}"; do
|
||||
local dir="${f%.*}"
|
||||
local extractor=""
|
||||
case "$f" in
|
||||
*.zip)
|
||||
extractor="_ununzip"
|
||||
;;
|
||||
*.tar.gz|*.tgz)
|
||||
extractor="_ungzip"
|
||||
;;
|
||||
|
||||
1)
|
||||
echo "No deletion on warnings "
|
||||
*.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"
|
||||
;;
|
||||
*)
|
||||
echo "[ zip file corrupted, failed ]"
|
||||
rm -rf $dir > /dev/null 2>&1
|
||||
disp I "File ${f} is not a supported archive, skipping."
|
||||
continue
|
||||
;;
|
||||
;; # Skip non-archive files
|
||||
esac
|
||||
|
||||
if [[ $createdir ]]; then
|
||||
echo -n "[ subdir created, "
|
||||
elif [[ $nodir ]]; then
|
||||
mv ./$dir/* ./ && rmdir $dir
|
||||
echo -n "[ No subdir, "
|
||||
# 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
|
||||
subdirs=$(find $dir -maxdepth 1 | wc -l)
|
||||
if [[ $subdirs -eq 2 ]]; then
|
||||
mv ./$dir/* ./ && rmdir $dir
|
||||
echo -n "[ No subdir, "
|
||||
else
|
||||
echo -n "[ subdir created, "
|
||||
fi
|
||||
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}..."
|
||||
mkdir -p "${dir}"
|
||||
[[ $? -gt 0 ]] &&
|
||||
disp E "The filesystem can't create directories, exit!" &&
|
||||
return 1
|
||||
|
||||
${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
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ -n ${createdir} ]]; then
|
||||
disp I "Archive extracted successfully in subdirectory."
|
||||
elif [[ -n ${nodir} ]]; then
|
||||
shopt -s nullglob
|
||||
for child in "${dir}"/*; do
|
||||
mv -- "$child" .
|
||||
done
|
||||
shopt -u nullglob
|
||||
rmdir -- "${dir}"
|
||||
disp I "Archive extracted successfully, no subdirectory needed."
|
||||
else
|
||||
# Set nullglob to ensure the array is empty if no files match
|
||||
shopt -s nullglob
|
||||
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."
|
||||
else
|
||||
disp I "Archive extracted successfully in subdirectory."
|
||||
fi
|
||||
shopt -u nullglob
|
||||
fi
|
||||
echo " OK ]"
|
||||
done
|
||||
done
|
||||
}
|
||||
export -f utaz
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Compress directories or files into one or more archive
|
||||
# ------------------------------------------------------------------------------
|
||||
taz ()
|
||||
# Usage: taz [option] [--parallel=<n>] [--format=<format>] [directory1 ... directoryN]
|
||||
# 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()
|
||||
{
|
||||
_doxz()
|
||||
{
|
||||
command -v xz >/dev/null 2>&1 || {
|
||||
echo -e >&2 "\t*** The program 'xz' is not installed, aborting."
|
||||
disp E "The program 'xz' is not installed, aborting."
|
||||
return 127
|
||||
}
|
||||
|
||||
[[ $4 ]] && local verb='-v'
|
||||
|
||||
# Display a warning for this format
|
||||
echo -e "\t! Warning: xz format is not suited for long term archiving."
|
||||
echo -e "\t See https://www.nongnu.org/lzip/xz_inadequate.html for details."
|
||||
disp W "xz format is not suited for long term archiving."
|
||||
disp I "See https://www.nongnu.org/lzip/xz_inadequate.html for details."
|
||||
|
||||
# Compresse to xz (lzma2) - Deprecated
|
||||
xz $verb --compress --keep -$3 -T $2 $1
|
||||
@@ -129,14 +415,14 @@ taz ()
|
||||
|
||||
command -v plzip >/dev/null 2>&1 || {
|
||||
command -v lzip >/dev/null 2>&1 || {
|
||||
echo -e >&2 "\t*** Program 'plzip' or 'lzip' are not installed, aborting."
|
||||
disp E "Program 'plzip' or 'lzip' are not installed, aborting."
|
||||
return 127
|
||||
}
|
||||
local command=lzip
|
||||
command=lzip
|
||||
local procopt=""
|
||||
[[ $2 -gt 1 ]] &&
|
||||
echo -e "\t! Warning: lzip doesn't support multithreading, falling back to 1 thread." &&
|
||||
echo -e "\t* Consitder installing plzip to obtain multithreading abilities."
|
||||
disp W "lzip doesn't support multithreading, falling back to 1 thread." &&
|
||||
disp W "Consider installing plzip to obtain multithreading abilities."
|
||||
}
|
||||
|
||||
[[ $4 ]] && local verb="-vv"
|
||||
@@ -153,14 +439,14 @@ taz ()
|
||||
|
||||
command -v pigz >/dev/null 2>&1 || {
|
||||
command -v gzip >/dev/null 2>&1 || {
|
||||
echo -e >&2 "\t*** Programs 'pigz' or 'gzip' are not installed, aborting."
|
||||
disp E "Programs 'pigz' or 'gzip' are not installed, aborting."
|
||||
return 127
|
||||
}
|
||||
local command="gzip --compress"
|
||||
local procopt=""
|
||||
[[ $2 -gt 1 ]] &&
|
||||
echo -e "\t! Warning: gzip doesn't support multithreading, falling back to 1 thread." &&
|
||||
echo -e "\t* Consitder installing pigz to obtain multithreading abilities."
|
||||
disp W "gzip doesn't support multithreading, falling back to 1 thread." &&
|
||||
disp W "Consider installing pigz to obtain multithreading abilities."
|
||||
}
|
||||
|
||||
[[ $4 ]] && local verb="--verbose"
|
||||
@@ -177,14 +463,14 @@ taz ()
|
||||
|
||||
command -v pbzip2 >/dev/null 2>&1 || {
|
||||
command -v bzip2 >/dev/null 2>&1 || {
|
||||
echo -e >&2 "\t*** The program 'pbzip2' or 'bzip2' are not installed, aborting."
|
||||
disp E "The program 'pbzip2' or 'bzip2' are not installed, aborting."
|
||||
return 127
|
||||
}
|
||||
local command=bzip2
|
||||
local procopt=""
|
||||
[[ $2 -gt 1 ]] &&
|
||||
echo -e "\t! Warning: bzip2 doesn't support multithreading, falling back to 1 thread." &&
|
||||
echo -e "\t* Consitder installing pbzip2 to obtain multithreading abilities."
|
||||
disp W "bzip2 doesn't support multithreading, falling back to 1 thread." &&
|
||||
disp W "Consider installing pbzip2 to obtain multithreading abilities."
|
||||
}
|
||||
|
||||
[[ $4 ]] && local verb="-v"
|
||||
@@ -197,98 +483,117 @@ taz ()
|
||||
_dolzo()
|
||||
{
|
||||
command -v lzop >/dev/null 2>&1 || {
|
||||
echo -e >&2 "\t*** The program 'lzop' is not installed, aborting."
|
||||
disp E "The program 'lzop' is not installed, aborting."
|
||||
return 127
|
||||
}
|
||||
|
||||
[[ $4 ]] && local verb='-v'
|
||||
[[ $2 -gt 1 ]] && echo -e "\t! Warning: 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
|
||||
lzop --keep -$3 $1
|
||||
return $?
|
||||
}
|
||||
|
||||
for opt in $@ ; do
|
||||
case $opt in
|
||||
"-h"|"--help")
|
||||
echo "taz: archive all files of a directory."
|
||||
echo
|
||||
echo "Usage: taz [option] [--parallel=<n>] [--format=<format>] [directory1 ... directoryN]"
|
||||
echo
|
||||
echo "Options:"
|
||||
echo " -h, --help Display that help screen"
|
||||
echo " -d, --delete Delete source file or directory after success"
|
||||
echo " -f, --format Chose archive format in the given list. If several format are"
|
||||
echo " given, the smalest is kept"
|
||||
echo " -p, --parallel Number of threads to use (if allowed by underlying utility)"
|
||||
echo " -v, --verbose Display progress where possible"
|
||||
echo " -1, .., -9 Compression level to use [1=fast/big, 9=slow/small]"
|
||||
echo
|
||||
echo "Supported archive format:"
|
||||
echo " Param.| programs | Algo. | Description"
|
||||
echo " ------+---------------+-------+----------------------------------------"
|
||||
echo " lz | plzip, lzip | lzma | Safe efficient default format"
|
||||
echo " xz | xz | lzma2 | Unsafe, not for long term"
|
||||
echo " bz2 | pbzip2, bzip2 | bzip2 | Historical but less efficient than lz"
|
||||
echo " gz | pigz, gzip | lz77 | Historical, safe, fast"
|
||||
echo " lzo | lzop | lzo | Very fast but no multithread"
|
||||
echo " tar | tar | tar | No compression"
|
||||
echo
|
||||
return 0
|
||||
;;
|
||||
local PARSED
|
||||
PARSED=$(getopt -o hdf:p:vq123456789 --long help,delete,format:,parallel:,verbose,quiet --name "taz" -- "$@")
|
||||
if [ $? -ne 0 ]; then
|
||||
disp E "Invalid options, use \"taz --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
eval set -- "$PARSED"
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "taz: archive all files of a directory.\n\n"
|
||||
printf "Usage: taz [option] [--parallel=<n>] [--format=<format>] [directory1 ... directoryN]\n\n"
|
||||
printf "Options:\n"
|
||||
printf "\t-h, --help\tDisplay that help screen\n"
|
||||
printf "\t-d, --delete\tDelete source file or directory after success\n"
|
||||
printf "\t-f, --format\tChose archive format in the given list. If several format are"
|
||||
printf "\t\t\tgiven, the smalest is kept\n"
|
||||
printf "\t-p, --parallel\tNumber of threads to use (if allowed by underlying utility)\n"
|
||||
printf "\t-v, --verbose\tDisplay progress where possible\n"
|
||||
printf "\t-q, --quiet\tDisplay less messages (only errors and warnings)\n"
|
||||
printf "\t-1, .., -9\tCompression level to use [1=fast/biggest, 9=slow/smallest]\n\n"
|
||||
printf "Supported archive format:\n"
|
||||
printf "\tParam.| programs | Algo. | Description\n"
|
||||
printf "\t------+---------------+-------+----------------------------------------\n"
|
||||
printf "\t lz | plzip, lzip | lzma | Safe efficient default format\n"
|
||||
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
|
||||
;;
|
||||
|
||||
"-d"|"--delete")
|
||||
local willrm=1
|
||||
;;
|
||||
-d|--delete)
|
||||
local willrm=1
|
||||
shift
|
||||
;;
|
||||
|
||||
"-f"?*|"--format"?*)
|
||||
local compform=$(echo "$opt" | cut -f 2- -d '=')
|
||||
;;
|
||||
-f|--format)
|
||||
local compform=$2
|
||||
shift 2
|
||||
;;
|
||||
|
||||
"-p"?*|"--parallel"?*)
|
||||
local nproc=$(echo "$opt" | cut -f 2- -d '=')
|
||||
;;
|
||||
-p|--parallel)
|
||||
local nproc=$2
|
||||
shift 2
|
||||
;;
|
||||
|
||||
"-v"|"--verbose")
|
||||
local verbose=1
|
||||
;;
|
||||
-v|--verbose)
|
||||
local verbose=1
|
||||
shift
|
||||
;;
|
||||
|
||||
"-"[1..9])
|
||||
local complevel=$(echo $opt | sed 's/-//')
|
||||
;;
|
||||
-q|--quiet)
|
||||
local quiet=1
|
||||
shift
|
||||
;;
|
||||
|
||||
"-"*)
|
||||
echo "Invalid option, use taz --help to display options list"
|
||||
echo
|
||||
return 1
|
||||
;;
|
||||
|
||||
*)
|
||||
local LIST="$LIST ${opt%/}"
|
||||
;;
|
||||
-[1-9])
|
||||
complevel="${1#-}"
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid option, use \"taz --help\" to display options list"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ ! $compform ]] && compform=lz # safe and efficient (unless data are already compressed)
|
||||
[[ ! $nproc ]] && nproc=1
|
||||
[[ ! $complevel ]] && complevel=6
|
||||
# The remaining arguments after -- are the files/directories to process,
|
||||
# "." is used if none is given
|
||||
local FILES=("$@")
|
||||
[[ ${#FILES[@]} -eq 0 ]] && FILES=(".")
|
||||
|
||||
for item in $LIST; do
|
||||
[[ ! $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."
|
||||
|
||||
for item in "${FILES[@]}"; do
|
||||
local donetar=0
|
||||
echo "--- Processing $item..."
|
||||
disp I "Processing $item..."
|
||||
|
||||
if [[ -d $item ]]; then
|
||||
echo -ne "\t* Creating $item.tar... "
|
||||
if [[ -d "$item" ]]; then
|
||||
disp I "\t Creating $item.tar... "
|
||||
|
||||
tar -cf $item{.tar,}
|
||||
tar -cf "$item.tar" "$item"
|
||||
if [[ ! $? -eq 0 ]]; then
|
||||
echo "[ failed, skipping ]"
|
||||
disp E "tar file creation failed, skipping to next item."
|
||||
continue
|
||||
fi
|
||||
|
||||
local donetar=1
|
||||
echo "[ OK ]"
|
||||
fi
|
||||
|
||||
local fname=$item
|
||||
@@ -296,29 +601,33 @@ taz ()
|
||||
|
||||
# Skip compression part if tar is asked
|
||||
if [[ $compform != "tar" ]]; then
|
||||
echo -e "\t* Compressing archive..."
|
||||
_do$compform $fname $nproc $complevel $verbose
|
||||
disp I "\t Compressing archive..."
|
||||
_do$compform "$fname" "$nproc" "$complevel" "$verbose"
|
||||
[[ ! $? -eq 0 ]] && case $? in
|
||||
127)
|
||||
echo -e "\t*** Compression program unavailable, aborting."
|
||||
return 127
|
||||
;;
|
||||
*)
|
||||
echo -e "\t*** Compression program returned an error, not deleting anything if asked, skipping to next item."
|
||||
continue
|
||||
;;
|
||||
127)
|
||||
disp E "Compression program unavailable, aborting."
|
||||
return 127
|
||||
;;
|
||||
*)
|
||||
disp E "Compression program returned an error, not deleting anything if asked, skipping to next item."
|
||||
continue
|
||||
;;
|
||||
esac
|
||||
|
||||
[[ $donetar -gt 0 ]] && rm $fname
|
||||
[[ $donetar -gt 0 ]] && rm "$fname"
|
||||
fi
|
||||
|
||||
if [[ $willrm ]]; then
|
||||
echo -en "\t* Deleting original source as asked... "
|
||||
rm -r $item && echo '[ OK ]' || echo '[ failed ]'
|
||||
disp I "\t Deleting original source as asked... "
|
||||
rm -r "$item"
|
||||
fi
|
||||
|
||||
echo "--- Done"
|
||||
done
|
||||
|
||||
unset quiet
|
||||
}
|
||||
export -f taz
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
load_conf "compress"
|
||||
|
||||
# EOF
|
||||
|
||||
@@ -1,72 +1,130 @@
|
||||
#!/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
|
||||
# ------------------------------------------------------------------------------
|
||||
function backtrace ()
|
||||
# Usage: backtrace
|
||||
function backtrace()
|
||||
{
|
||||
echo "========= Call stack ========="
|
||||
typeset -i i=0
|
||||
|
||||
local func=
|
||||
for func in "${FUNCNAME[@]}"; do
|
||||
if [[ $i -ne 0 ]]; then
|
||||
printf '%15s() %s:%d\n' \
|
||||
"$func" "${BASH_SOURCE[$i]}" "${BASH_LINENO[ (( $i - 1)) ]}"
|
||||
fi
|
||||
let i++ || true
|
||||
printf "========= Call stack =========\n"
|
||||
local i=1 # We begin at 1 to ignore backtrace itself
|
||||
while [[ $i -lt ${#FUNCNAME[@]} ]]; do
|
||||
printf '%15s() %s:%d\n' \
|
||||
"${FUNCNAME[$i]}" "${BASH_SOURCE[$i]}" "${BASH_LINENO[$(( i-1 ))]}"
|
||||
((i++))
|
||||
done
|
||||
unset func i
|
||||
echo "=============================="
|
||||
unset i
|
||||
printf "==============================\n"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Function to be trapped for errors investigation
|
||||
# ------------------------------------------------------------------------------
|
||||
function error ()
|
||||
function error()
|
||||
{
|
||||
local errcode=$?
|
||||
backtrace
|
||||
return $errcode
|
||||
}
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Activate or deactivate error trapping to display backtrace
|
||||
# ------------------------------------------------------------------------------
|
||||
settrace ()
|
||||
# Usage: settrace <--on|--off|--status>
|
||||
settrace()
|
||||
{
|
||||
local status="off"
|
||||
[[ $(trap -p ERR) ]] && status="on"
|
||||
#trap -p ERR
|
||||
for opt in $@ ; do
|
||||
case $opt in
|
||||
"-h"|"--help")
|
||||
echo "Try to activate backtrace display for script debugging."
|
||||
echo
|
||||
echo "Options:"
|
||||
echo " --on Activate backtrace generation"
|
||||
echo " --off Deactivate backtrace generation"
|
||||
echo
|
||||
echo "That function active a trap event on error. If the script you want to"
|
||||
echo "debug overload the ERR bash trap, it will not work."
|
||||
echo
|
||||
;;
|
||||
"--on")
|
||||
if [[ $status == "on" ]]; then
|
||||
echo "Warning: ERR signal trap is already set, replacing previous trap!"
|
||||
fi
|
||||
trap "error" ERR
|
||||
;;
|
||||
"--off")
|
||||
if [[ $status != "on" ]]; then
|
||||
echo "Warning: ERR signal trap is already unset!"
|
||||
fi
|
||||
trap - ERR
|
||||
;;
|
||||
"--status")
|
||||
echo "ERR trap signal is ${status}."
|
||||
;;
|
||||
esac
|
||||
|
||||
local PARSED
|
||||
PARSED=$(getopt -oh --long help,on,off,status,force -- "$@")
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"settrace --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
eval set -- "$PARSED"
|
||||
local force=0
|
||||
while true; do
|
||||
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)
|
||||
if [[ ${status} == "on" ]] && [[ $force -eq 0 ]]; then
|
||||
disp E "ERR signal trap is already set. Use --force to replace it."
|
||||
return 1
|
||||
fi
|
||||
trap "error" ERR
|
||||
shift
|
||||
;;
|
||||
--force)
|
||||
force=1
|
||||
shift
|
||||
;;
|
||||
--off)
|
||||
if [[ ${status} != "on" ]]; then
|
||||
disp W "ERR signal trap is already unset!"
|
||||
fi
|
||||
trap - ERR
|
||||
shift
|
||||
;;
|
||||
--status)
|
||||
disp I "Trap signal is ${status}."
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid options, use \"settrace --help\" to display usage."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
unset status
|
||||
unset status force
|
||||
}
|
||||
export -f settrace
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# EOF
|
||||
|
||||
183
profile.d/disp.sh
Normal file
183
profile.d/disp.sh
Normal file
@@ -0,0 +1,183 @@
|
||||
#!/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.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Color definitions
|
||||
# Standard 16 colors display declaration
|
||||
export DEFAULTFG='\e[0;39m'
|
||||
export DEFAULTBG='\e[0;49m'
|
||||
export DEFAULTCOL="${DEFAULTBG}${DEFAULTFG}"
|
||||
export RESETCOL=$'\e[0m'
|
||||
|
||||
# Regular Colors
|
||||
export Black='\e[0;30m'
|
||||
export Red='\e[0;31m'
|
||||
export Green='\e[0;32m'
|
||||
export Yellow='\e[0;33m'
|
||||
export Blue='\e[0;34m'
|
||||
export Purple='\e[0;35m'
|
||||
export Cyan='\e[0;36m'
|
||||
export White='\e[0;37m'
|
||||
|
||||
# Bold
|
||||
export BBlack='\e[1;30m'
|
||||
export BRed='\e[1;31m'
|
||||
export BGreen='\e[1;32m'
|
||||
export BYellow='\e[1;33m'
|
||||
export BBlue='\e[1;34m'
|
||||
export BPurple='\e[1;35m'
|
||||
export BCyan='\e[1;36m'
|
||||
export BWhite='\e[1;37m'
|
||||
|
||||
# Underline
|
||||
export UBlack='\e[4;30m'
|
||||
export URed='\e[4;31m'
|
||||
export UGreen='\e[4;32m'
|
||||
export UYellow='\e[4;33m'
|
||||
export UBlue='\e[4;34m'
|
||||
export UPurple='\e[4;35m'
|
||||
export UCyan='\e[4;36m'
|
||||
export UWhite='\e[4;37m'
|
||||
|
||||
# Background
|
||||
export On_Black='\e[40m'
|
||||
export On_Red='\e[41m'
|
||||
export On_Green='\e[42m'
|
||||
export On_Yellow='\e[43m'
|
||||
export On_Blue='\e[44m'
|
||||
export On_Purple='\e[45m'
|
||||
export On_Cyan='\e[46m'
|
||||
export On_White='\e[47m'
|
||||
|
||||
# High Intensity
|
||||
export IBlack='\e[0;90m'
|
||||
export IRed='\e[0;91m'
|
||||
export IGreen='\e[0;92m'
|
||||
export IYellow='\e[0;93m'
|
||||
export IBlue='\e[0;94m'
|
||||
export IPurple='\e[0;95m'
|
||||
export ICyan='\e[0;96m'
|
||||
export IWhite='\e[0;97m'
|
||||
|
||||
# Bold High Intensity
|
||||
export BIBlack='\e[1;90m'
|
||||
export BIRed='\e[1;91m'
|
||||
export BIGreen='\e[1;92m'
|
||||
export BIYellow='\e[1;93m'
|
||||
export BIBlue='\e[1;94m'
|
||||
export BIPurple='\e[1;95m'
|
||||
export BICyan='\e[1;96m'
|
||||
export BIWhite='\e[1;97m'
|
||||
|
||||
# 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'
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Display a message
|
||||
# Usage: disp <type> <message>
|
||||
# Types:
|
||||
# I : info (green)
|
||||
# W : warning (yellow)
|
||||
# E : error (red)
|
||||
# D : debug (cyan)
|
||||
disp()
|
||||
{
|
||||
# Handle NO_COLOR: disable colors if set
|
||||
local color_enabled=1
|
||||
[[ -n $NO_COLOR ]] && color_enabled=0
|
||||
|
||||
case ${1^^} in
|
||||
"I")
|
||||
if [[ $color_enabled -eq 1 ]]; then
|
||||
local heads="[ ${IGreen}info${DEFAULTFG} ]"
|
||||
else
|
||||
local heads="[ info ]"
|
||||
fi
|
||||
shift
|
||||
[[ -z $QUIET || $QUIET -ne 1 ]] && \
|
||||
printf "%b\n" "${heads} $*${RESETCOL}"
|
||||
;;
|
||||
"W")
|
||||
if [[ $color_enabled -eq 1 ]]; then
|
||||
local heads="[ ${IYellow}Warning${DEFAULTFG} ]"
|
||||
else
|
||||
local heads="[ Warning ]"
|
||||
fi
|
||||
shift
|
||||
printf "%b\n" "${heads} $*${RESETCOL}" >&2
|
||||
;;
|
||||
"E")
|
||||
if [[ $color_enabled -eq 1 ]]; then
|
||||
local heads="[ ${IRed}ERROR${DEFAULTFG} ]"
|
||||
else
|
||||
local heads="[ ERROR ]"
|
||||
fi
|
||||
shift
|
||||
printf "%b\n" "${heads} $*${RESETCOL}" >&2
|
||||
;;
|
||||
"D")
|
||||
if [[ $color_enabled -eq 1 ]]; then
|
||||
local heads="[ ${ICyan}debug${DEFAULTFG} ]"
|
||||
else
|
||||
local heads="[ debug ]"
|
||||
fi
|
||||
shift
|
||||
[[ -n $DEBUG && $DEBUG -gt 1 ]] && \
|
||||
printf "%b\n" "${heads} $*${RESETCOL}"
|
||||
;;
|
||||
* )
|
||||
[[ -z $QUIET || $QUIET -ne 1 ]] && \
|
||||
printf "%b\n" "$*"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
export -f disp
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# Load disp section variables
|
||||
load_conf disp
|
||||
|
||||
# EOF
|
||||
@@ -1,179 +1,797 @@
|
||||
#!/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()
|
||||
{
|
||||
local result=""
|
||||
for item in "$1"; do
|
||||
for content in "$item"; do
|
||||
result+="\"$content\" "
|
||||
done
|
||||
done
|
||||
echo $result
|
||||
}
|
||||
local separator="${EXPANDLIST_DEFAULT_SEPARATOR:- }"
|
||||
local PARSED
|
||||
|
||||
PARSED=$(getopt -o hs:n --long help,separator:,newline -n 'expandlist' -- "$@")
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"expandlist --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
eval set -- "$PARSED"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Clean a directory or a tree from temporary or backup files
|
||||
# ------------------------------------------------------------------------------
|
||||
clean ()
|
||||
{
|
||||
for opt in $@ ; do
|
||||
case $opt in
|
||||
"-r"|"--recurs")
|
||||
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
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "expandlist: expand globs and wrap matched items in double quotes.\n\n"
|
||||
printf "Usage: expandlist [options] <item1 [item2 ... itemN]>\n\n"
|
||||
printf "Options:\n"
|
||||
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||
printf "\t-s, --separator SEP\tSet output separator (default: space)\n"
|
||||
printf "\t-n, --newline\t\tUse a newline as separator\n"
|
||||
return 0
|
||||
;;
|
||||
|
||||
"-s"|"--shell")
|
||||
local outshell=1
|
||||
-s|--separator)
|
||||
separator="$2"
|
||||
shift 2
|
||||
;;
|
||||
|
||||
"-f"|"--force")
|
||||
local force=1
|
||||
-n|--newline)
|
||||
separator=$'\n'
|
||||
shift
|
||||
;;
|
||||
|
||||
"-"*)
|
||||
echo "Invalid option, use \"clean --help\" to display usage."
|
||||
echo
|
||||
return 1
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
|
||||
*)
|
||||
local dirlist="$dirlist $opt"
|
||||
disp E "Invalid options, use \"expandlist --help\" to display usage."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ ! $dirlist ]] && local dirlist=$(pwd)
|
||||
local item="" result="" matched=0
|
||||
shopt -s nullglob
|
||||
|
||||
[[ ! $recursive ]] && local findopt="-maxdepth 1"
|
||||
[[ ! $force ]] && local rmopt="-i"
|
||||
unset recursive force
|
||||
for item in "$@"; do
|
||||
local expanded=()
|
||||
|
||||
for dir in $dirlist; do
|
||||
local dellist=$(find $dir $findopt -type f -name "*~" -o -name "#*#" \
|
||||
-o -name "*.bak" -o -name ".~*#")
|
||||
for f in $dellist; do
|
||||
if [[ ! $outshell ]]; then
|
||||
rm $rmopt $f
|
||||
else
|
||||
echo "rm $rmopt $f"
|
||||
# True glob expansion when wildcards are present.
|
||||
if [[ "$item" == *'*'* || "$item" == *'?'* || "$item" == *'['* ]]; then
|
||||
expanded=( $item )
|
||||
else
|
||||
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
|
||||
unset outshell dirlist dellist findopt rmopt
|
||||
|
||||
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' -- "$@")
|
||||
|
||||
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
|
||||
done
|
||||
done
|
||||
}
|
||||
export -f clean
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Create a directory then goes inside
|
||||
# ------------------------------------------------------------------------------
|
||||
mcd ()
|
||||
# Usage: mcd <directory>
|
||||
mcd()
|
||||
{
|
||||
if [[ ! $# -eq 1 ]] ; then
|
||||
echo "Create a directory then goes inside."
|
||||
echo "Usage: mcd <directory>"
|
||||
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
|
||||
printf "mcd: Create a directory and enter it.\n\n"
|
||||
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
|
||||
mkdir -pv $1 && cd $1
|
||||
mkdir -pv "$1" && cd "$1" || {
|
||||
printf "Failed create and/or change directory.\n"
|
||||
return 1
|
||||
}
|
||||
}
|
||||
export -f mcd
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Rename all files in current directory to replace spaces with _
|
||||
# ------------------------------------------------------------------------------
|
||||
rmspc ()
|
||||
# Rename files and directories to replace spaces with another character
|
||||
# Usage: rmspc [options]
|
||||
# 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=""
|
||||
for opt in $@ ; do
|
||||
case $opt in
|
||||
"-h"|"--help")
|
||||
echo "rmspc: remove spaces from all filenames in current directories"
|
||||
echo
|
||||
echo "Usage: rmspc [option]"
|
||||
echo
|
||||
echo "Options:"
|
||||
echo " -h, --help Display that help screen"
|
||||
echo " -r, --recursive Treat subdirectories of the given directory"
|
||||
echo " -c, --subst-char Change the replacement character (default is underscore)"
|
||||
echo " -v, --verbose Display what is being done"
|
||||
echo " -s, --shell Do nothing and display commands that would be executed"
|
||||
echo
|
||||
echo "Note: if the --subst-char option is given without parameters, spaces will be"
|
||||
echo " replaced with nothing (concatenation)."
|
||||
echo
|
||||
return 0
|
||||
local recurs=0 verb=0 shell=0
|
||||
local substchar="${RMSPC_DEFAULT_CHAR:-_}" substchar_set=0
|
||||
local mvopt=()
|
||||
local PARSED
|
||||
|
||||
PARSED=$(getopt -o hr:c::vs --long help,recursive,subst-char::,verbose,shell -n 'rmspc' -- "$@")
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"rmspc --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
eval set -- "$PARSED"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "rmspc: remove spaces from all filenames in current directories\n\n"
|
||||
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
|
||||
;;
|
||||
|
||||
"-r"|"--recursive")
|
||||
local recurs=1
|
||||
-r|--recursive)
|
||||
recurs=1
|
||||
shift
|
||||
;;
|
||||
|
||||
"-c"?*|"--subst-char"?*)
|
||||
if [[ $(echo $opt | grep "=") ]]; then
|
||||
local substchar=$(echo "$opt" | cut -f 2- -d '=')
|
||||
else
|
||||
local substchar='none'
|
||||
fi
|
||||
;;
|
||||
|
||||
"-v"|"--verbose")
|
||||
local verb=1
|
||||
-c|--subst-char)
|
||||
substchar_set=1
|
||||
substchar="$2"
|
||||
shift 2
|
||||
;;
|
||||
|
||||
"-s"|"--shell")
|
||||
local shell=1
|
||||
-v|--verbose)
|
||||
verb=1
|
||||
shift
|
||||
;;
|
||||
-s|--shell)
|
||||
shell=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Invalid parameter, use \"rmspc --help\" to display options list"
|
||||
echo
|
||||
disp E "Invalid parameter, use \"rmspc --help\" to display options list"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ ! $substchar ]] && substchar="_"
|
||||
[[ $substchar == "none" ]] && local substchar=""
|
||||
[[ $verb ]] && local mvopt="-v"
|
||||
[[ "$substchar" == "none" ]] && substchar=""
|
||||
(( verb )) && mvopt=(-v)
|
||||
|
||||
shopt -s nullglob
|
||||
for f in *; do
|
||||
[[ $recurs ]] && [[ -d "$f" ]] && (
|
||||
[[ $verb ]] && echo "-- Entering directory $(pwd)/$f ..."
|
||||
local lastdir=$f
|
||||
pushd "$f" > /dev/null
|
||||
rmspc $@
|
||||
popd > /dev/null
|
||||
[[ $verb ]] && echo "-- Leaving directory $(pwd)/$lastdir"
|
||||
unset lastdir
|
||||
)
|
||||
if (( recurs )) && [[ -d "$f" ]]; then
|
||||
(
|
||||
local lastdir=$f
|
||||
(( verb )) && disp I "Entering directory $(pwd)/$f ..."
|
||||
pushd "$f" >/dev/null || return 1
|
||||
|
||||
if [[ $(echo $f | grep " ") ]]; then
|
||||
if (( substchar_set )); then
|
||||
rmspc ${recurs:+-r} -c "$substchar" ${verb:+-v} ${shell:+-s}
|
||||
else
|
||||
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}}"
|
||||
local command="mv $mvopt \"$f\" \"$newf\""
|
||||
if [[ $shell ]]; then
|
||||
echo $command
|
||||
[[ "$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
|
||||
$command
|
||||
mv "${mvopt[@]}" -- "$f" "$newf" || {
|
||||
disp E "Failed renaming \"$f\" to \"$newf\"."
|
||||
continue
|
||||
}
|
||||
fi
|
||||
fi
|
||||
done
|
||||
unset lst substchar verb shell newf command mvopt
|
||||
shopt -u nullglob
|
||||
}
|
||||
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' -- "$@")
|
||||
|
||||
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
|
||||
shift
|
||||
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' -- "$@")
|
||||
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' -- "$@")
|
||||
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' -- "$@")
|
||||
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
|
||||
|
||||
114
profile.d/fun.sh
Normal file
114
profile.d/fun.sh
Normal file
@@ -0,0 +1,114 @@
|
||||
#!/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' -- "$@")
|
||||
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=$(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,88 @@
|
||||
#!/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
|
||||
# ------------------------------------------------------------------------------
|
||||
# Usage: help
|
||||
help()
|
||||
{
|
||||
cat <<EOF
|
||||
clean Erase backup files
|
||||
dpkgs Search for the given package in the installed ones
|
||||
gpid Give the list of PIDs for the given process name
|
||||
isipv4 Tell if the given IPv4 is valid
|
||||
isipv6 Tell if the given IPv6 is valid
|
||||
ku Kill process owned by users in parameter
|
||||
mcd Create a directory and go inside
|
||||
meteo Display curent weather forecast for the configured city
|
||||
ppg Display process matching the given parameter
|
||||
rain Let the rain fall
|
||||
rmhost Remove host (IP and/or DNS name) for current known_host
|
||||
rmspc Remove spaces from all the files in working directory
|
||||
setc Set console language to C
|
||||
setfr Set console language to French
|
||||
settrace Activate/deactivate call trace for script debugging
|
||||
setus Set console language to US English
|
||||
showinfo Show the welcoming baner with basic system information
|
||||
ssr Do a root login to the given address
|
||||
taz Compress smartly the given files or directory
|
||||
utaz Uncompress all zip files in the given (or current) directory
|
||||
ver Display version of your copy of profile
|
||||
printf "${BIWhite}Welcome to your profile! Here is a list of available commands:${DEFAULTCOL}\n\n"
|
||||
printf "check_updates\tCheck for new versions of profile\n"
|
||||
printf "clean\t\tErase backup files\n"
|
||||
printf "disp\t\tDisplay formatted info/warning/error/debug messages\n"
|
||||
printf "dwl\t\tDownload a URL to a local file\n"
|
||||
printf "expandlist\tExpand and quote item lists\n"
|
||||
printf "file_stats\tDisplay file size statistics for a path\n"
|
||||
printf "findbig\t\tFind biggest files in the given (or current) directory\n"
|
||||
printf "finddead\tFind dead symbolic links in the given (or current) directory\n"
|
||||
printf "findzero\tFind empty files in the given (or current) directory\n"
|
||||
printf "genpwd\t\tGenerate secure passwords\n"
|
||||
printf "gpid\t\tGive the list of PIDs for the given process name\n"
|
||||
printf "isipv4\t\tTell if the given IPv4 is valid\n"
|
||||
printf "isipv6\t\tTell if the given IPv6 is valid\n"
|
||||
printf "ku\t\tKill process owned by users in parameter\n"
|
||||
printf "matrix\t\tDisplay matrix-style digital rain\n"
|
||||
printf "mcd\t\tCreate a directory and go inside\n"
|
||||
printf "meteo\t\tDisplay current weather forecast for the configured city\n"
|
||||
printf "myextip\tDisplay current external/public IP\n"
|
||||
printf "pkgs\t\tSearch for the given package in installed ones\n"
|
||||
printf "ppg\t\tDisplay process matching the given parameter\n"
|
||||
printf "ppn\t\tDisplay process matching the exact process name given in parameter\n"
|
||||
printf "ppu\t\tDisplay processes owned by the given user\n"
|
||||
printf "profile_upgrade\tUpgrade profile to the latest version\n"
|
||||
printf "pwdscore\tCalculate password strength score\n"
|
||||
printf "rain\t\tLet the rain fall\n"
|
||||
printf "rmhost\t\tRemove host (IP and/or DNS name) from current known_hosts\n"
|
||||
printf "rmspc\t\tRemove spaces from file and directory names\n"
|
||||
printf "setlocale\tSet console language to the current locale\n"
|
||||
printf " * setc\tSet console language to C\n"
|
||||
printf " * setfr\tSet console language to French\n"
|
||||
printf " * setus\tSet console language to US English\n"
|
||||
printf "settrace\tActivate/deactivate call trace for script debugging\n"
|
||||
printf "showinfo\tShow welcome banner with basic system information\n"
|
||||
printf "ssr\t\tDo a root login to the given address\n"
|
||||
printf "taz\t\tCompress smartly the given files or directory\n"
|
||||
printf "urlencode\tURL-encode the given text\n"
|
||||
printf "utaz\t\tUncompress archives in the given (or current) directory\n"
|
||||
printf "ver\t\tDisplay version of your copy of profile\n\n"
|
||||
|
||||
Please use <command> --help to obtain usage details.
|
||||
EOF
|
||||
printf "\nPlease use <command> --help to obtain usage details.\n"
|
||||
}
|
||||
export -f help
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# EOF
|
||||
|
||||
@@ -1,55 +1,196 @@
|
||||
#!/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
|
||||
# ------------------------------------------------------------------------------
|
||||
ver ()
|
||||
# Usage: ver
|
||||
ver()
|
||||
{
|
||||
echo "Profile version $PROFVERSION."
|
||||
local PARSED
|
||||
|
||||
PARSED=$(getopt -o h --long help -n 'ver' -- "$@")
|
||||
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."
|
||||
}
|
||||
export -f ver
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Display weather of the given city (or default one)
|
||||
# ------------------------------------------------------------------------------
|
||||
meteo ()
|
||||
# Display weather for the given city (or the default one)
|
||||
# Usage: meteo [city1 city2 ...]
|
||||
meteo()
|
||||
{
|
||||
cities=$@
|
||||
[[ $# -eq 0 ]] && local cities=$DEFAULT_CITY
|
||||
local PARSED
|
||||
|
||||
for city in $cities; do
|
||||
curl https://wttr.in/$city || echo "Failed fetching datas for $city."
|
||||
PARSED=$(getopt -o h --long help -n 'meteo' -- "$@")
|
||||
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
|
||||
}
|
||||
export -f meteo
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Display system general information
|
||||
# ------------------------------------------------------------------------------
|
||||
# Usage: showinfo
|
||||
showinfo()
|
||||
{
|
||||
echo -e "\n"
|
||||
if command -v figlet >/dev/null 2>&1; then
|
||||
if [[ -s /usr/share/figlet/ansi_shadow.flf ]]; then
|
||||
local figopt="-f ansi_shadow"
|
||||
fi
|
||||
figlet -k $(hostname) $figopt
|
||||
else
|
||||
echo "$(hostname -f)"
|
||||
local PARSED
|
||||
|
||||
PARSED=$(getopt -o h --long help -n 'showinfo' -- "$@")
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"showinfo --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
echo ""
|
||||
if command -v neofetch >/dev/null 2>&1; then
|
||||
neofetch
|
||||
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
|
||||
[[ -s /usr/share/figlet/ansi_shadow.flf ]] && \
|
||||
figopt=(-f ansi_shadow)
|
||||
figlet -k "${figopt[@]}" "$hostname_str"
|
||||
else
|
||||
(
|
||||
if [[ -s /etc/os-release ]]; then
|
||||
. /etc/os-release
|
||||
echo "$NAME $VERSION"
|
||||
else
|
||||
cat /proc/version
|
||||
fi
|
||||
echo "Uptime: $(uptime)"
|
||||
)
|
||||
printf "%s\n" "$hostname_str"
|
||||
fi
|
||||
|
||||
printf "\n"
|
||||
if command -v neofetch >/dev/null 2>&1; then
|
||||
neofetch
|
||||
elif command -v fastfetch >/dev/null 2>&1; then
|
||||
fastfetch
|
||||
else
|
||||
(
|
||||
if [[ -s /etc/os-release ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
. /etc/os-release
|
||||
printf "%s %s\n" "$NAME" "$VERSION"
|
||||
else
|
||||
cat /proc/version
|
||||
fi
|
||||
printf "Uptime: %s\n" "$(uptime -p)"
|
||||
)
|
||||
fi
|
||||
}
|
||||
export -f showinfo
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
load_conf info
|
||||
# EOF
|
||||
|
||||
@@ -1,35 +1,181 @@
|
||||
#!/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
|
||||
export LANG=fr_FR.UTF-8
|
||||
export LC_MESSAGES=fr_FR.UTF-8
|
||||
export LC_ALL=fr_FR.UTF-8
|
||||
locale -a | grep -qx "$1" || {
|
||||
disp W "Locale '$1' is not installed on this system."
|
||||
return 1
|
||||
}
|
||||
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' -- "$@")
|
||||
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
|
||||
export LANG=C
|
||||
export LC_MESSAGES=C
|
||||
export LC_ALL=C
|
||||
disp I "Locale changed to standard C (POSIX)."
|
||||
}
|
||||
export -f setc
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Change locale to US (needed by Steam)
|
||||
# ------------------------------------------------------------------------------
|
||||
setus ()
|
||||
# Build dynamic locale shortcuts from SET_LOCALE
|
||||
# Expected format:
|
||||
# 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
|
||||
export LANG=en_US.UTF-8
|
||||
export LC_MESSAGES=en_US.UTF-8
|
||||
export LC_ALL=en_US.UTF-8
|
||||
local cfg="${SET_LOCALE:-}"
|
||||
local item="" alias="" loc="" fname=""
|
||||
local -a locale_items=()
|
||||
|
||||
[[ -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
|
||||
|
||||
304
profile.d/net.sh
304
profile.d/net.sh
@@ -1,49 +1,299 @@
|
||||
#!/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() { [ -z "$output" ] && curl -sL "$url" || curl -sL -o "$output" "$url"; }
|
||||
_try_wget() { [ -z "$output" ] && wget -qO- "$url" || wget -q -O "$output" "$url"; }
|
||||
_try_fetch() { [ -z "$output" ] && fetch -o - "$url" || fetch -o "$output" "$url"; }
|
||||
|
||||
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
|
||||
# ------------------------------------------------------------------------------
|
||||
isipv4 ()
|
||||
# Usage: isipv4 <ip_address>
|
||||
isipv4()
|
||||
{
|
||||
# Set up local variables
|
||||
local ip=$1
|
||||
local ip=$1
|
||||
[[ -z $ip ]] && return 1
|
||||
|
||||
# Start with a regex format test
|
||||
if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
||||
local old_ifs=$IFS
|
||||
IFS="."
|
||||
ip=($ip)
|
||||
IFS=$old_ifs
|
||||
if [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
|
||||
&& ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]; then
|
||||
if [[ -t 1 ]]; then
|
||||
echo "The given IPv4 is valid."
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
if [[ -t 1 ]]; then
|
||||
echo "The given parameter is NOT a valid IPv4."
|
||||
# Start with a regex format test (four octets)
|
||||
if [[ $ip =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
|
||||
local old_ifs=$IFS
|
||||
IFS='.'
|
||||
read -r -a ip_arr <<< "$ip"
|
||||
IFS=$old_ifs
|
||||
|
||||
# Ensure each octet is between 0 and 255
|
||||
local oct
|
||||
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
|
||||
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
|
||||
fi
|
||||
|
||||
[[ -t 1 ]] && disp "The given parameter is NOT a valid IPv4."
|
||||
return 1
|
||||
}
|
||||
export -f isipv4
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Determine if parameter is a valid IPv4 address
|
||||
# ------------------------------------------------------------------------------
|
||||
isipv6 ()
|
||||
# Determine if parameter is a valid IPv6 address
|
||||
# Usage: isipv6 <ip_address>
|
||||
isipv6()
|
||||
{
|
||||
local ip="$1"
|
||||
local regex='^([0-9a-fA-F]{0,4}:){1,7}[0-9a-fA-F]{0,4}$'
|
||||
if [[ $ip =~ $regex ]]; then
|
||||
if [[ -t 1 ]]; then
|
||||
echo "The given IPv6 is valid."
|
||||
fi
|
||||
return 0
|
||||
if [[ -t 1 ]]; then
|
||||
disp "The given IPv6 is valid."
|
||||
fi
|
||||
return 0
|
||||
fi
|
||||
if [[ -t 1 ]]; then
|
||||
echo "The given parameter is not a valid IPv6."
|
||||
disp "The given parameter is not a valid IPv6."
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
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,101 @@
|
||||
#!/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.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Look for a package within installed one
|
||||
# ------------------------------------------------------------------------------
|
||||
dpkgs ()
|
||||
# Usage: dpkgs <string>
|
||||
pkgs()
|
||||
{
|
||||
local count=0
|
||||
for opt in $@ ; do
|
||||
case $opt in
|
||||
"-h"|"--help")
|
||||
echo "dpkgs: look for an installed package by it's name."
|
||||
echo
|
||||
echo "Usage: dpkgs <string>"
|
||||
local ignore_case=${PKGS_DEFAULT_IGNORE_CASE:-0}
|
||||
|
||||
local PARSED
|
||||
PARSED=$(getopt -o hi --long help,ignore-case -n 'pkgs' -- "$@")
|
||||
if [[ $? -ne 0 ]]; then
|
||||
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
|
||||
;;
|
||||
|
||||
"-"*)
|
||||
echo "Invalid option, use \"dpkgs --help\" to display usage."
|
||||
echo
|
||||
return 1
|
||||
-i|--ignore-case)
|
||||
ignore_case=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
|
||||
*)
|
||||
local pkg=$1 && shift
|
||||
count=$(( $count + 1 ))
|
||||
[[ $count -gt 1 ]] &&
|
||||
echo "*** Error: Please specify a package name, without space, eventually partial." &&
|
||||
return 1
|
||||
|
||||
disp E "Invalid option: $1"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
[[ $count -lt 1 ]] &&
|
||||
echo "*** Error: 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
|
||||
}
|
||||
|
||||
[[ -x /usr/sbin/dpkg ]] &&
|
||||
echo "*** Error: dpkg command seems unavialable." &&
|
||||
# Build grep command
|
||||
local grep_opt=""
|
||||
(( ignore_case )) && grep_opt="-i"
|
||||
|
||||
command -v dpkg >/dev/null 2>&1 && local cmd="dpkg -l"
|
||||
command -v rpm >/dev/null 2>&1 && local cmd="rpm -qa"
|
||||
if [[ -z $cmd ]]; then
|
||||
disp E "No usable package manager seems unavialable."
|
||||
return 2
|
||||
|
||||
dpkg -l | grep $pkg
|
||||
fi
|
||||
$cmd | grep $grep_opt $pkg
|
||||
}
|
||||
export -f dpkgs
|
||||
export -f pkgs
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
load_conf "packages"
|
||||
|
||||
# EOF
|
||||
|
||||
@@ -1,40 +1,255 @@
|
||||
#!/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
|
||||
# ------------------------------------------------------------------------------
|
||||
ppg ()
|
||||
# Usage: ppg <string>
|
||||
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
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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'
|
||||
ps -eo pid,comm | grep -w "$1"
|
||||
}
|
||||
export -f ppn
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Get PID list of the given process name
|
||||
# ------------------------------------------------------------------------------
|
||||
gpid ()
|
||||
# Usage: ppid <process_name [process_name2 ...]>
|
||||
gpid()
|
||||
{
|
||||
[[ $UID -eq 0 ]] && local psopt="-A"
|
||||
[[ $# -eq 1 ]] && local single=1
|
||||
for pid in $@; do
|
||||
local result=$(ps $psopt | grep $pid | awk '{print $1}' | sed "s/\n/ /")
|
||||
if [[ $single ]]; then
|
||||
[[ $result ]] && echo "${result//$'\n'/ }"
|
||||
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
|
||||
printf "gpid: Get PID list of the given process name.\n\n"
|
||||
printf "Usage: gpid <process_name [process_name2 ...]>\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: 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
|
||||
[[ $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
|
||||
done
|
||||
[[ $result ]] || return 1
|
||||
|
||||
(( found )) || return 1
|
||||
}
|
||||
export -f gpid
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Kill all processes owned by the given users
|
||||
# ------------------------------------------------------------------------------
|
||||
ku ()
|
||||
# Kill all processes owned by the given users (kill user)
|
||||
# Usage: ku <username1 [username2 ...]>
|
||||
ku()
|
||||
{
|
||||
for u in $@; do
|
||||
killall -u $u
|
||||
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
|
||||
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
|
||||
local users="$@"
|
||||
for u in $users; 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
|
||||
}
|
||||
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: ppg <string>"
|
||||
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=$(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,16 +1,189 @@
|
||||
#!/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
|
||||
_lth_value="${_lth_value#\"}" ; _lth_value="${_lth_value%\"}"
|
||||
_lth_value="${_lth_value#\'}" ; _lth_value="${_lth_value%\'}"
|
||||
|
||||
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"
|
||||
}
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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
|
||||
{
|
||||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
local delta_us=$((($(timer_now) - $timer_start) / 1000))
|
||||
@@ -39,52 +212,79 @@ function timer_stop
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Function triguered internaly by bash : defining prompt
|
||||
# ------------------------------------------------------------------------------
|
||||
set_prompt ()
|
||||
# Function triggered internally by bash : defining prompt
|
||||
# Usage: 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!
|
||||
Blue='\[\e[0;34m\]'
|
||||
White='\[\e[01;37m\]'
|
||||
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'
|
||||
local Last_Command=$? # Must come first!
|
||||
local FancyX='\342\234\227'
|
||||
local Checkmark='\342\234\223'
|
||||
|
||||
# Resolve theme/config colours with hardcoded fallbacks
|
||||
local _time_fg="${PROMPT_COLOR_TIME_FG:-$Blue}"
|
||||
local _time_bg="${PROMPT_COLOR_TIME_BG:-$On_IBlack}"
|
||||
local _bar_bg="${PROMPT_COLOR_BAR_BG:-$On_Blue}"
|
||||
local _ok_fg="${PROMPT_COLOR_OK_FG:-$White}"
|
||||
local _ok_mark="${PROMPT_COLOR_OK_MARK:-$Green}"
|
||||
local _err_bg="${PROMPT_COLOR_ERR_BG:-$On_Red}"
|
||||
local _err_fg="${PROMPT_COLOR_ERR_FG:-$White}"
|
||||
local _err_mark="${PROMPT_COLOR_ERR_MARK:-$BYellow}"
|
||||
local _root_fg="${PROMPT_COLOR_ROOT_FG:-$Red}"
|
||||
local _user_fg="${PROMPT_COLOR_USER_FG:-$Green}"
|
||||
local _dir_fg="${PROMPT_COLOR_DIR_FG:-$ICyan}"
|
||||
|
||||
# Begin with time
|
||||
PS1="\[\e[s$Blue$OnGrey [ \t ] $OnBlue"
|
||||
PS1="\[\e[s${_time_fg}${_time_bg} [ \t ] ${_bar_bg}"
|
||||
|
||||
# Add a bright white exit status for the last command
|
||||
|
||||
# If it was successful, print a green check mark. Otherwise, print
|
||||
# a red X.
|
||||
# Add exit status of the last command.
|
||||
# If it was successful, print a green check mark. Otherwise, print a red X.
|
||||
if [[ $Last_Command == 0 ]]; then
|
||||
PS1+="$White$OnBlue [ \$Last_Command "
|
||||
PS1+="$Green$Checkmark "
|
||||
PS1+="${_ok_fg}${_bar_bg} [ \$Last_Command "
|
||||
PS1+="${_ok_mark}${Checkmark} "
|
||||
else
|
||||
PS1+="$White$OnRed [ \$Last_Command "
|
||||
PS1+="$Yellow$FancyX "
|
||||
PS1+="${_err_fg}${_err_bg} [ \$Last_Command "
|
||||
PS1+="${_err_mark}${FancyX} "
|
||||
fi
|
||||
|
||||
# Add the ellapsed time and current date
|
||||
# Add the elapsed time
|
||||
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 root, print the host in root colour. Otherwise use user colour.
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
PS1+="$Red\\u$Green@\\h"
|
||||
PS1+="${_root_fg}\\u${_user_fg}@\\h"
|
||||
else
|
||||
PS1+="$Green\\u@\\h"
|
||||
PS1+="${_user_fg}\\u@\\h"
|
||||
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.
|
||||
PS1+="$ICyan\\w \\\$$Default "
|
||||
PS1+="\e[K\e[u$DEFAULTCOL\n"
|
||||
# Print the working directory and prompt marker, then reset colour.
|
||||
PS1+="${_dir_fg}\\w \\\$$DEFAULTCOL "
|
||||
}
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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
|
||||
|
||||
596
profile.d/pwd.sh
Executable file → Normal file
596
profile.d/pwd.sh
Executable file → Normal file
@@ -1,144 +1,482 @@
|
||||
#!/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
|
||||
# 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
|
||||
# ------------------------------------------------------------------------------
|
||||
genpwd()
|
||||
{
|
||||
local length=16
|
||||
local occurs=2 # Bug, if set to 1, seems to be ignored
|
||||
local symb=1 maj=1 min=1 numb=1
|
||||
local nbpwd=1
|
||||
local length=${GENPWD_DEFAULT_LENGTH:-16}
|
||||
local occurs=${GENPWD_DEFAULT_OCCURS:-2}
|
||||
local symb=${GENPWD_DEFAULT_SYMBOLS:-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
|
||||
case $opt in
|
||||
"-h"|"--help")
|
||||
echo "genpwd: generate a secure random password."
|
||||
echo
|
||||
echo "Usage: genpwd [options] [--extracars=<cars>] [--length=<n>] [nb_passwd]"
|
||||
echo
|
||||
echo "Options:"
|
||||
echo " -h, --help Display that help screen"
|
||||
echo " -s, --nosymbols Exclude symbols"
|
||||
echo " -n, --nonumbers Exclude numbers"
|
||||
echo " -u, --noup Exclude uppercase letters"
|
||||
echo " -l, --nolow Exclude lowercase letters"
|
||||
echo " -e=<c>, --extracars=<c>"
|
||||
echo " Add the given caracters to the possible caracter list"
|
||||
echo " -L=<n>, --length=<n>"
|
||||
echo " Set length of the password (default is $length)"
|
||||
echo " -o=<n>, --occurences=<n>"
|
||||
echo " Set the maximum occurences of a same caracter (default is $occurs)"
|
||||
echo
|
||||
echo "If the --extracars parameter is given, at least one of the given caracter will"
|
||||
echo "be used in the final password."
|
||||
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
|
||||
;;
|
||||
"-s"|"--nosymbols")
|
||||
symb=0
|
||||
;;
|
||||
"-n"|"--nonumbers")
|
||||
numb=0
|
||||
;;
|
||||
"-u"|"--noup")
|
||||
maj=0
|
||||
;;
|
||||
"-l"|"--nolow")
|
||||
min=0
|
||||
;;
|
||||
"-e"?*|"--extracars"?*)
|
||||
local extcar=$(echo "$opt" | cut -f 2- -d '=')
|
||||
;;
|
||||
"-L"?*|"--length"?*)
|
||||
local length=$(echo "$opt" | cut -f 2- -d '=')
|
||||
if ! [[ $length =~ ^[0-9]+$ ]]; then
|
||||
echo "The --length parameter requires a number."
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
"-o"?*|"--occurences"?*)
|
||||
local occurs=$(echo "$opt" | cut -f 2- -d '=')
|
||||
if ! [[ $occurs =~ ^[1-9]+$ ]]; then
|
||||
echo "The --occurs parameter requires a number from 1 to 9."
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
"-*")
|
||||
echo "Unknow parameter ${opt}."
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
if ! [[ $opt =~ ^[1-9]+$ ]]; then
|
||||
echo "Unknow parameter ${opt}."
|
||||
return 1
|
||||
else
|
||||
local nbpwd=$opt
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
local PARSED
|
||||
PARSED=$(getopt -o hsnule:L:o: --long \
|
||||
help,nosymbols,nonumbers,noup,nolow,extracars:,length:,occurences:,occurrences: \
|
||||
-n 'genpwd' -- "$@")
|
||||
if [[ $? -ne 0 ]]; then return 1; fi
|
||||
eval set -- "$PARSED"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "genpwd: Generate random password(s).\n\n"
|
||||
printf "Usage: genpwd [options] [nb_passwd]\n\n"
|
||||
printf "Options:\n"
|
||||
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||
printf "\t-s, --nosymbols\t\tExclude symbols\n"
|
||||
printf "\t-n, --nonumbers\t\tExclude numbers\n"
|
||||
printf "\t-u, --noup\t\tExclude uppercase letters\n"
|
||||
printf "\t-l, --nolow\t\tExclude lowercase letters\n"
|
||||
printf "\t-e, --extracars <c>\tAdd characters to the pool\n"
|
||||
printf "\t-L, --length <n>\tSet password length (default: 16)\n"
|
||||
printf "\t-o, --occurences <n>\tMax occurrences per character (default: 2)\n"
|
||||
return 0
|
||||
;;
|
||||
-s|--nosymbols)
|
||||
symb=0
|
||||
shift
|
||||
;;
|
||||
-n|--nonumbers)
|
||||
numb=0
|
||||
shift
|
||||
;;
|
||||
-u|--noup)
|
||||
maj=0
|
||||
shift
|
||||
;;
|
||||
-l|--nolow)
|
||||
min=0
|
||||
shift
|
||||
;;
|
||||
-e|--extracars)
|
||||
extcar="$2"
|
||||
shift 2
|
||||
;;
|
||||
-L|--length)
|
||||
length="$2"
|
||||
if ! [[ $length =~ ^[1-9][0-9]*$ ]]; then
|
||||
disp E "The --length parameter requires a positive integer."
|
||||
return 1
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
-o|--occurences|--occurrences)
|
||||
occurs="$2"
|
||||
if ! [[ $occurs =~ ^[1-9][0-9]*$ ]]; then
|
||||
disp E "The --occurences parameter requires a positive integer."
|
||||
return 1
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid options, use \"genpwd --help\" to display usage."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Function selecting a random caracter from the list in parameter
|
||||
pickcar()
|
||||
{
|
||||
# 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"
|
||||
if [[ $# -gt 1 ]]; then
|
||||
disp E "Too many positional arguments. Use only [nb_passwd]."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ $# -eq 1 ]]; then
|
||||
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
|
||||
|
||||
echo "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
|
||||
if [[ -z $carset ]]; then
|
||||
disp E "No characters are available. Re-enable at least one character class."
|
||||
return 1
|
||||
fi
|
||||
|
||||
# ?, *, $ 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
|
||||
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
|
||||
unset seen_chars
|
||||
carset="$unique_carset"
|
||||
|
||||
for i in $( seq 1 $(( $length - $rlength )) ); do
|
||||
pickcar "$carset"
|
||||
done
|
||||
} | sort -R | awk '{printf "%s", $1}'
|
||||
unset picked carset rlength
|
||||
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
|
||||
}
|
||||
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 rain_cars=("|" "│" "┃" "┆" "┇" "┊" "┋" "╽" "╿")
|
||||
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
|
||||
local base_color="$1"
|
||||
RAIN_ENGINE_COLORS=()
|
||||
|
||||
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() {
|
||||
term_width=$(tput cols)
|
||||
term_height=$(tput lines)
|
||||
#step_duration=0.025
|
||||
(( max_rain_width = term_width * term_height / 4 ))
|
||||
(( max_rain_height = term_height < 10 ? 1 : term_height / 10 ))
|
||||
# In percentage
|
||||
(( 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 ))
|
||||
_rain_build_chars()
|
||||
{
|
||||
local mode="$1"
|
||||
local charset="$2"
|
||||
RAIN_ENGINE_CHARS=()
|
||||
|
||||
case "$mode" in
|
||||
matrix)
|
||||
case "$charset" in
|
||||
""|binary)
|
||||
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
|
||||
}
|
||||
|
||||
do_render() {
|
||||
# Clean screen first
|
||||
local idx=0
|
||||
do_render()
|
||||
{
|
||||
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
|
||||
X=${rains[idx]}
|
||||
Y=${rains[idx + 1]}
|
||||
drop_length=${rains[idx + 4]}
|
||||
for ((y = Y; y < Y + drop_length; y++)); do
|
||||
(( y < 1 || y > term_height )) && continue
|
||||
echo -ne "\e[${y};${X}H "
|
||||
((y < 1 || y > term_height)) && continue
|
||||
printf "\e[%d;%dH " "$y" "$X"
|
||||
done
|
||||
done
|
||||
|
||||
for ((idx = 0; idx < num_rains * num_rain_metadata; idx += num_rain_metadata)); do
|
||||
if (( 100 * RANDOM / 32768 < falling_odd )); then
|
||||
# Falling
|
||||
if (( ++rains[idx + 1] > term_height )); then
|
||||
# Out of screen, bye sweet <3
|
||||
rains=("${rains[@]:0:idx}"
|
||||
"${rains[@]:idx+num_rain_metadata:num_rains*num_rain_metadata}")
|
||||
(( num_rains-- ))
|
||||
if ((100 * RANDOM / 32768 < falling_odd)); then
|
||||
if ((++rains[idx + 1] > term_height)); then
|
||||
rains=("${rains[@]:0:idx}" "${rains[@]:idx+num_rain_metadata:num_rains*num_rain_metadata}")
|
||||
((num_rains--))
|
||||
continue
|
||||
fi
|
||||
fi
|
||||
|
||||
X=${rains[idx]}
|
||||
Y=${rains[idx + 1]}
|
||||
rain_drop=${rains[idx + 2]}
|
||||
drop_color=${rains[idx + 3]}
|
||||
drop_length=${rains[idx + 4]}
|
||||
|
||||
for ((y = Y; y < Y + drop_length; y++)); do
|
||||
(( y < 1 || y > term_height )) && continue
|
||||
echo -ne "\e[${y};${X}H${drop_color}${rain_drop}"
|
||||
((y < 1 || y > term_height)) && continue
|
||||
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
|
||||
}
|
||||
|
||||
trap do_exit TERM INT
|
||||
trap sigwinch WINCH
|
||||
# No echo stdin and hide the cursor
|
||||
stty -echo
|
||||
echo -ne "\e[?25l"
|
||||
printf "\e[?25l"
|
||||
printf "\e[2J"
|
||||
|
||||
echo -ne "\e[2J"
|
||||
local rains=()
|
||||
local num_rains=0
|
||||
local ch=""
|
||||
|
||||
sigwinch
|
||||
while (( exit_st <= 0 )); do
|
||||
if (( $exit_st <=0 )); then
|
||||
read -n 1 -t $step_duration ch
|
||||
case "$ch" in
|
||||
q|Q)
|
||||
do_exit
|
||||
while ((exit_st <= 0)); do
|
||||
read -r -n 1 -t "$frame_sleep" ch
|
||||
case "$ch" in
|
||||
q|Q)
|
||||
do_exit
|
||||
;;
|
||||
esac
|
||||
esac
|
||||
|
||||
if (( num_rains < max_rain_width )) && (( 100 * RANDOM / 32768 < new_rain_odd )); then
|
||||
# Need new |, 1-based
|
||||
rain_drop="${rain_cars[rain_tab * RANDOM / 32768]}"
|
||||
drop_color="${rain_colors[rain_color_tab * RANDOM / 32768]}"
|
||||
drop_length=$(( max_rain_height * RANDOM / 32768 + 1 ))
|
||||
X=$(( term_width * RANDOM / 32768 + 1 ))
|
||||
Y=$(( 1 - drop_length ))
|
||||
rains=( "${rains[@]}" "$X" "$Y" "$rain_drop" "$drop_color" "$drop_length" )
|
||||
(( num_rains++ ))
|
||||
fi
|
||||
|
||||
# Let rain fall!
|
||||
do_render
|
||||
if ((num_rains < max_rain_width)) && ((100 * RANDOM / 32768 < new_rain_odd)); then
|
||||
rain_drop="${rain_chars[rain_tab * RANDOM / 32768]}"
|
||||
drop_color="${rain_colors[rain_color_tab * RANDOM / 32768]}"
|
||||
drop_length=$((max_rain_height * RANDOM / 32768 + 1))
|
||||
X=$((term_width * RANDOM / 32768 + 1))
|
||||
Y=$((1 - drop_length))
|
||||
rains=("${rains[@]}" "$X" "$Y" "$rain_drop" "$drop_color" "$drop_length")
|
||||
((num_rains++))
|
||||
fi
|
||||
done
|
||||
echo -ne "\e[${term_height};1H\e[0K"
|
||||
|
||||
# Show cursor and echo stdin
|
||||
echo -ne "\e[?25h"
|
||||
do_render
|
||||
done
|
||||
|
||||
printf "\e[%d;1H\e[0K" "$term_height"
|
||||
printf "\e[?25h"
|
||||
stty echo
|
||||
unset exit_st
|
||||
trap - TERM INT
|
||||
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
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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
|
||||
|
||||
245
profile.d/ssh.sh
245
profile.d/ssh.sh
@@ -1,70 +1,205 @@
|
||||
#!/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
|
||||
echo "Error: incorrect number of parameters."
|
||||
echo "Usage: rmhost <hostname|ip> [hostname2|ip2 [...]]"
|
||||
return 1
|
||||
fi
|
||||
local PARSED
|
||||
local all_users=0
|
||||
local -a known_hosts_files=()
|
||||
|
||||
while [[ $1 ]]; do
|
||||
local hst=$1 && shift
|
||||
isipv4 $hst > /dev/null
|
||||
local v4=$?
|
||||
isipv6 $hst > /dev/null
|
||||
local v6=$?
|
||||
PARSED=$(getopt -o ha --long help,all-users -n 'rmhost' -- "$@")
|
||||
if [[ $? -ne 0 ]]; then return 1; fi
|
||||
eval set -- "$PARSED"
|
||||
|
||||
if [[ $v4 -eq 0 || $v6 -eq 0 ]]; then
|
||||
local ip=$hst
|
||||
unset hst
|
||||
fi
|
||||
unset v4 v6
|
||||
|
||||
if [[ ! $ip && $hst ]]; then
|
||||
ip=$(host $hst | grep "has address" | awk '{print $NF}')
|
||||
[[ ! $? ]] &&
|
||||
echo "*** rmhost(): Error extracting IP from hostname." &&
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ $hst ]]; then
|
||||
echo "Removing host $hst from ssh known_host..."
|
||||
ssh-keygen -R $hst > /dev/null
|
||||
fi
|
||||
if [[ $ip ]]; then
|
||||
echo "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]>"
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "rmhost: Remove host/IP from known_hosts files.\n\n"
|
||||
printf "Usage: rmhost [--all-users] <hostname|ip> [hostname2|ip2 ...]\n\n"
|
||||
printf "Options:\n"
|
||||
printf " -a, --all-users Remove entries from all local users when run as root\n"
|
||||
printf " -h, --help Display this help screen\n"
|
||||
return 0
|
||||
;;
|
||||
-a|--all-users)
|
||||
all_users=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid options, use \"rmhost --help\" to display usage."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ ! $1 ]] &&
|
||||
echo "Please specify the server you want to log in." &&
|
||||
[[ $# -eq 0 ]] && {
|
||||
disp E "Missing argument. Use 'rmhost --help' for usage."
|
||||
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
|
||||
|
||||
ssh "${ssh_default_opts[@]}" root@"$srv" "$@"
|
||||
}
|
||||
export -f ssr
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
load_conf "ssh"
|
||||
|
||||
# EOF
|
||||
|
||||
42
profile.d/themes/abyss.theme
Executable file
42
profile.d/themes/abyss.theme
Executable 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="$On_Black" # deep black background
|
||||
PROMPT_COLOR_BAR_BG="$On_Blue" # deep blue bar
|
||||
|
||||
PROMPT_COLOR_OK_FG="$ICyan" # teal on success
|
||||
PROMPT_COLOR_OK_MARK="$IGreen" # bright teal-green checkmark
|
||||
|
||||
PROMPT_COLOR_ERR_BG="$On_Red" # red background on failure
|
||||
PROMPT_COLOR_ERR_FG="$IWhite" # bright white text
|
||||
PROMPT_COLOR_ERR_MARK="$IYellow" # golden X
|
||||
|
||||
PROMPT_COLOR_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
Executable file
42
profile.d/themes/adwaita.theme
Executable 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="$White" # white text
|
||||
PROMPT_COLOR_ERR_MARK="$Yellow" # yellow X (warning intent)
|
||||
|
||||
PROMPT_COLOR_ROOT_FG="$Red" # Adwaita red for root
|
||||
PROMPT_COLOR_USER_FG="$IBlue" # Adwaita blue for user
|
||||
PROMPT_COLOR_DIR_FG="$IGreen" # Adwaita green for path
|
||||
32
profile.d/themes/dark.theme
Executable file
32
profile.d/themes/dark.theme
Executable 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_Black" # Clock background (black)
|
||||
PROMPT_COLOR_BAR_BG="$On_IBlack" # 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
Executable file
32
profile.d/themes/default.theme
Executable 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_IBlack" # 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
Executable file
35
profile.d/themes/light.theme
Executable 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="$Blue" # Clock text (ICyan → Blue, darker for light bg)
|
||||
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
Executable file
64
profile.d/themes/monochrome.theme
Executable 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
Executable file
45
profile.d/themes/monokai.theme
Executable 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
Executable file
42
profile.d/themes/plasma.theme
Executable 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="$IPurple" # vivid purple clock text
|
||||
PROMPT_COLOR_TIME_BG="$On_IBlack" # dark grey background
|
||||
PROMPT_COLOR_BAR_BG="$On_Purple" # bright magenta bar
|
||||
|
||||
PROMPT_COLOR_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
Executable file
127
profile.d/themes/solarized-light.theme
Executable 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
Executable file
122
profile.d/themes/solarized.theme
Executable 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;7;54;66m" # Base02 — bar background
|
||||
|
||||
PROMPT_COLOR_OK_FG="\e[38;2;131;148;150m" # Base0 — body text on success
|
||||
PROMPT_COLOR_OK_MARK="\e[38;2;133;153;0m" # Green — checkmark
|
||||
|
||||
PROMPT_COLOR_ERR_BG="\e[48;2;220;50;47m" # Red — error background
|
||||
PROMPT_COLOR_ERR_FG="\e[38;2;253;246;227m" # Base3 — bright fg on red
|
||||
PROMPT_COLOR_ERR_MARK="\e[38;2;181;137;0m" # Yellow — X mark on red bg
|
||||
|
||||
PROMPT_COLOR_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
|
||||
417
profile.d/updates.sh
Normal file
417
profile.d/updates.sh
Normal file
@@ -0,0 +1,417 @@
|
||||
#!/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' -- "$@")
|
||||
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' -- "$@")
|
||||
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
|
||||
303
profile.sh
303
profile.sh
@@ -1,42 +1,7 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# Begin profile
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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>
|
||||
# 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
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright (c) 2013-2022 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||
# 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,
|
||||
@@ -70,46 +35,163 @@
|
||||
# * OF SUCH DAMAGE.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
export PROFVERSION="3.1.0"
|
||||
|
||||
export DEFAULT_CITY="Toulouse"
|
||||
|
||||
if [[ ! $(echo $SHELL | grep bash) ]]; then
|
||||
echo "That script is designed to be used with bash as being the shell."
|
||||
echo "Please consider using bash instead, or patch me ;) !"
|
||||
if [[ ! $SHELL =~ bash ]]; then
|
||||
echo "That environment script is designed to be used with bash being the shell."
|
||||
echo "Please consider using bash to enjoy our features!"
|
||||
return 1
|
||||
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 1 2>/dev/null || exit 1
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# path* : private functions for PATH variable management
|
||||
# ------------------------------------------------------------------------------
|
||||
pathremove ()
|
||||
pathremove()
|
||||
{
|
||||
local ifs=':'
|
||||
local newpath
|
||||
local dir
|
||||
local pathvar=${2:-PATH}
|
||||
for dir in ${!pathvar} ; do
|
||||
if [ "$dir" != "$1" ] ; then
|
||||
newpath=${newpath:+$newpath:}$dir
|
||||
[[ -z "$1" ]] && return 0
|
||||
local IFS=':'
|
||||
local newpath dir
|
||||
local pathvar="${2:-PATH}"
|
||||
[[ "$pathvar" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] || {
|
||||
printf "pathremove: unsafe variable name '%s'\n" "$pathvar" >&2
|
||||
return 1
|
||||
}
|
||||
for dir in ${!pathvar}; do
|
||||
[[ "$dir" != "$1" ]] && newpath="${newpath:+$newpath:}$dir"
|
||||
done
|
||||
export "$pathvar=$newpath"
|
||||
}
|
||||
|
||||
pathprepend()
|
||||
{
|
||||
[[ -z "$1" ]] && return 0
|
||||
local pathvar="${2:-PATH}"
|
||||
[[ "$pathvar" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] || {
|
||||
printf "pathprepend: unsafe variable name '%s'\n" "$pathvar" >&2
|
||||
return 1
|
||||
}
|
||||
pathremove "$1" "$pathvar"
|
||||
export "$pathvar=$1${!pathvar:+:${!pathvar}}"
|
||||
}
|
||||
|
||||
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 line 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"
|
||||
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
|
||||
alias "$key"="$cmd"
|
||||
fi
|
||||
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 pathvar=${2:-PATH}
|
||||
export $pathvar="${!pathvar:+${!pathvar}:}$1"
|
||||
local section_name="CONF_$1"
|
||||
|
||||
[[ "$(declare -p "$section_name" 2>/dev/null)" != "declare -A"* ]] && return 1
|
||||
|
||||
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
|
||||
}
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -118,75 +200,59 @@ 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
|
||||
export MYPATH=$(dirname "$(realpath -s "${BASH_SOURCE[0]}")")
|
||||
else
|
||||
export MYPATH="$PROFILE_PATH"
|
||||
fi
|
||||
if [[ ! -e "$MYPATH/profile.sh" ]]; then
|
||||
echo "[ Warning ] Path detection failed, trying to use pwd..."
|
||||
MYPATH=$(pwd)
|
||||
if [[ ! -e "$MYPATH/profile.sh" ]]; then
|
||||
echo "[ Error ] Impossible to determine installation path, pretty much nothing will work."
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -s "$MYPATH/version" ]]; then
|
||||
echo "[ Warning ] Impossible to determine running version of profile, your installation might be broken."
|
||||
fi
|
||||
export PROFVERSION=$(cat "$MYPATH"/version)
|
||||
|
||||
# Build PATH environment variable
|
||||
if [[ $EUID -eq 0 ]] ; then
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
pathappend /sbin:/usr/sbin
|
||||
fi
|
||||
[[ -d /share/services/gestparc ]] && pathappend /share/services/gestparc
|
||||
[[ -d ~/bin ]] && pathappend ~/bin
|
||||
[[ -d ~/.local/bin ]] && pathappend ~/.local/bin
|
||||
|
||||
# Set bash history
|
||||
export HISTSIZE=50000
|
||||
export HISTIGNORE="&:[bf]g:exit"
|
||||
# Parse and load general configuration
|
||||
export PROFILE_CONF="$MYPATH/profile.conf"
|
||||
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
|
||||
export PAGER=less
|
||||
|
||||
# More colors
|
||||
export TERM=xterm-256color
|
||||
|
||||
# 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
|
||||
# Load module scripts
|
||||
shopt -s nullglob
|
||||
for script in "$MYPATH/profile.d/"*.sh; do
|
||||
if [[ -f "$script" && -r "$script" ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
. "$script" || printf "[ Warning ] Failed to source module: %s\n" "$script" >&2
|
||||
fi
|
||||
done
|
||||
shopt -u nullglob
|
||||
|
||||
# Interactive shell detection, two methods available each one of those might have different result
|
||||
# depending on distribution
|
||||
#shopt -q login_shell && INTERACTIVE=1
|
||||
[[ $- == *i* ]] && INTERACTIVE=1
|
||||
[[ $- == *i* ]] && export INTERACTIVE=1
|
||||
|
||||
if [[ $INTERACTIVE ]]; then
|
||||
# For compiling (as we often compile with LFS/0linux...)
|
||||
#Aliases
|
||||
alias ll='ls -laFh --color=auto'
|
||||
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'
|
||||
load_alias aliases
|
||||
|
||||
# Define PS1
|
||||
trap 'timer_start' DEBUG
|
||||
@@ -194,8 +260,9 @@ if [[ $INTERACTIVE ]]; then
|
||||
|
||||
# Set default language
|
||||
setfr
|
||||
showinfo
|
||||
echo "Profile version $PROFVERSION chargé..."
|
||||
showinfo && printf "\n"
|
||||
check_updates -q
|
||||
disp I "Profile version $PROFVERSION chargé..."
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
|
||||
Reference in New Issue
Block a user