Compare commits
109 Commits
128cfe8c87
...
4.x
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ec52aa49f | ||
|
|
1b28e90c62 | ||
|
|
1e31712b60 | ||
|
|
5faae67d11 | ||
|
|
ee72ede116 | ||
|
|
f5244ac062 | ||
|
|
9a089112c3 | ||
|
|
e64a857a43 | ||
|
|
ddd7d4193a | ||
|
|
83a1c8ce48 | ||
|
|
b29fa3b30c | ||
|
|
cd0bcfd214 | ||
|
|
a91c41871a | ||
|
|
9698f0e506 | ||
|
|
9e22f007b9 | ||
|
|
d472fb61aa | ||
|
|
9108ee8266 | ||
|
|
a7f7452b2b | ||
|
|
bc67399ebc | ||
|
|
02b037d0fc | ||
|
|
e567957ea0 | ||
|
|
67bdd3e863 | ||
|
|
fa573bce8f | ||
|
|
241d53ebc4 | ||
|
|
1225230a07 | ||
|
|
9c43190202 | ||
|
|
9477638a28 | ||
|
|
f16ad711fb | ||
|
|
15ef317029 | ||
|
|
a6e4d7a256 | ||
|
|
6106ca7684 | ||
|
|
1088029ae6 | ||
|
|
eb4c89759b | ||
|
|
1dc5d72ac6 | ||
|
|
d49703c5d5 | ||
|
|
066f2e353e | ||
|
|
c011f03aee | ||
|
|
c4b0516c45 | ||
|
|
4da29872fc | ||
|
|
d7a0e2c5f5 | ||
|
|
d60a93814b | ||
|
|
d27935eedd | ||
|
|
e77232b3ca | ||
|
|
d6dce2d91e | ||
|
|
4e3cccff64 | ||
|
|
91f033743d | ||
|
|
9fcf21c55e | ||
|
|
5300386941 | ||
|
|
dfad345be3 | ||
|
|
1b16878ea8 | ||
|
|
30b8b8241a | ||
|
|
a4056b9e82 | ||
|
|
6696e0d05a | ||
|
|
6d15af029e | ||
|
|
bc0a592fa1 | ||
|
|
b3f909e287 | ||
|
|
89e20993da | ||
|
|
9a006883b8 | ||
|
|
85f02f4498 | ||
|
|
bb9bbfda16 | ||
|
|
9ea1cc3f72 | ||
|
|
b79103a0a6 | ||
|
|
e5bafe1721 | ||
|
|
322d03ed4c | ||
|
|
60a159c3ea | ||
|
|
84e6fdd429 | ||
|
|
8fe11776cb | ||
|
|
0737d0c647 | ||
|
|
d72fa1a712 | ||
|
|
08e9e6c799 | ||
|
|
ac66e896dd | ||
|
|
0712be626b | ||
|
|
3f8b81562b | ||
|
|
96d1dc695d | ||
|
|
c039ab6ea0 | ||
|
|
6b85556a53 | ||
|
|
cf9a85e61b | ||
|
|
75e047d70e | ||
|
|
742ec484a7 | ||
|
|
1b7262c0cd | ||
|
|
e387209c10 | ||
|
|
f5d59ec194 | ||
|
|
2ee1c935ac | ||
|
|
e41c1a4c51 | ||
|
|
60dfe19049 | ||
|
|
c32771a4ff | ||
|
|
080511d0bd | ||
|
|
d8bdfefdf1 | ||
|
|
5f5f9c0e71 | ||
|
|
30387a4f08 | ||
|
|
0c51363d86 | ||
|
|
043fbaef0b | ||
|
|
ed5587712e | ||
|
|
58cc76c317 | ||
|
|
e82ee06e1d | ||
|
|
bc8cb4a237 | ||
|
|
ae90a9f4c4 | ||
|
|
7e661ca2de | ||
|
|
9f22ed4304 | ||
|
|
1484b004be | ||
|
|
0a4206b890 | ||
|
|
02a1e25df2 | ||
|
|
7ca0a6fb88 | ||
|
|
25df408e37 | ||
|
|
3eab1f98d5 | ||
|
|
6c895b509a | ||
|
|
2ece711e1a | ||
|
|
39a7e7b40f | ||
|
|
6d5d872b71 |
15
.gitignore
vendored
Normal file
15
.gitignore
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
# User-specific configuration — never commit personal settings.
|
||||
# Copy doc/profile.conf.example to profile.conf and customise it.
|
||||
profile.conf
|
||||
|
||||
# Compiled / generated artefacts
|
||||
*.pyc
|
||||
__pycache__/
|
||||
|
||||
# macOS noise
|
||||
.DS_Store
|
||||
|
||||
# Editor swap / backup files
|
||||
*~
|
||||
*.swp
|
||||
*.swo
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright 2013-2022 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||
Copyright 2013-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||
|
||||
This is distributed with BSD-3-Clause license with the following terms and
|
||||
condition:
|
||||
|
||||
442
README.md
442
README.md
@@ -1,110 +1,402 @@
|
||||
# profile
|
||||
This project aims to create an advanced bash profile. It includes some aliases,
|
||||
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 administrator but might satisfy some regular users.
|
||||
targeted to system administrators but might satisfy some regular users.
|
||||
|
||||
## 1. Getting started
|
||||
## 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):
|
||||
```
|
||||
directory.
|
||||
|
||||
The profile is designed to be **sourced**, not executed directly.
|
||||
|
||||
Manual setup:
|
||||
```bash
|
||||
source <installpath>/profile/profile.sh
|
||||
```
|
||||
|
||||
It's not recommended to load that profile in /etc/profile as the users' .bashrc
|
||||
Automatic setup (recommended):
|
||||
```bash
|
||||
bash <installpath>/profile/profile.sh --install
|
||||
```
|
||||
|
||||
`--install` appends the required `source` line to both `~/.bashrc` and
|
||||
`~/.profile` by default. You can target one file only:
|
||||
|
||||
```bash
|
||||
bash <installpath>/profile/profile.sh --install --bashrc
|
||||
bash <installpath>/profile/profile.sh --install --profile
|
||||
```
|
||||
|
||||
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. What's the purpose?
|
||||
profile is giving access to numerous functions, aliases and to an advanced
|
||||
prompt. Here is a non-exhaustive list of what we have:
|
||||
- A bar style prompt with hour, execution time and exit code of the last
|
||||
command;
|
||||
- clean: erase after confirmation any backup file, possibly recursively;
|
||||
- dpkgs: search for the given pattern in the installed packages name;
|
||||
- expandlist: usefull in scripts, it expand any expression using wildcards into
|
||||
the corresponding list of file and directories;
|
||||
- genpwd: generate one or more random secure password;
|
||||
- gpid: give the list of PID matching the given process name;
|
||||
- help: display the list of available function and basic use;
|
||||
- isipv4: tell if the given parameter is a valid IPv4 address;
|
||||
- isipv6: tell if the given parameter is a valid IPv6 address;
|
||||
- ku: kill all the processes owned by the given user name or ID;
|
||||
- mcd: create a directory and immediately move into it;
|
||||
- meteo: display weather forecast information;
|
||||
- ppg: look for the given patern in the running processes;
|
||||
- rain: console screensaver with rain effect;
|
||||
- rmhost: remove the given host (name or IP) to the list of SSH known host;
|
||||
- rmspc: in the current directory it replace all the spaces in filenames with a
|
||||
underscore caracter (or any other given in option);
|
||||
- setc: set locale on standard C;
|
||||
- setfr: set locale on French;
|
||||
- settrace: allow the debugging of any script by showing a backtrace in case of
|
||||
error;
|
||||
- setus: set locale on US English;
|
||||
- showinfo: display basic informations about the host;
|
||||
- ssr: root ssh login to the given host;
|
||||
- taz: a universal command to compress files and directories, possibly several
|
||||
at once;
|
||||
- utaz: a utility that smartly uncompress many archives at once, creating a
|
||||
directory only if needed;
|
||||
- ver: show profile version.
|
||||
### 2.1. Interactive vs non-interactive shells
|
||||
|
||||
## 3. Configuration
|
||||
Some functions might have configurable default behaviour. You can create a
|
||||
.profile.conf file to configure those default behaviour. You should have a look
|
||||
at the doc/.profile.conf.example to see the list of available options.
|
||||
`profile.sh` detects whether the current shell is interactive.
|
||||
|
||||
## 4. Contact and more information
|
||||
### 4.1. New users
|
||||
This project is very new in terms of publication, and I have no idea of who will
|
||||
use it, if any does. If you use (or plan to use) ```profile```, I'll be very
|
||||
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!
|
||||
In interactive shells (typical terminal sessions), profile enables
|
||||
interactive-only features such as:
|
||||
- aliases from `[aliases]`
|
||||
- bash completion scripts from `profile.d/bash-completion/`
|
||||
- prompt initialization (`PROMPT_COMMAND` and timer hook)
|
||||
- welcome display (`showinfo`) and startup update check (`check_updates -q`)
|
||||
|
||||
### 4.2. Bugs
|
||||
In non-interactive shells (typical script execution), those features are
|
||||
intentionally skipped to avoid side effects and startup noise. Public functions
|
||||
remain available after sourcing, so scripts can still call profile helpers.
|
||||
|
||||
### 2.2. 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; when called with `-q` at startup a 3-second network timeout is applied so a slow or absent network never delays the prompt |
|
||||
| `clean` | filefct | Erase backup files in given directories, optionally recursive |
|
||||
| `disp` | disp | Display formatted info / warning / error / debug messages; long messages are word-wrapped and continuation lines are indented to align with the message text |
|
||||
| `dwl` | net | Download a URL using curl, wget, or fetch transparently; supports `-t <seconds>` / `--timeout <seconds>` to cap the transfer time |
|
||||
| `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 |
|
||||
| `gacp` | git | Add, commit and push changes; auto-pulls with rebase first if needed |
|
||||
| `genpwd` | pwd | Generate one or more random secure passwords with configurable constraints |
|
||||
| `ggraph` | git | Display a decorated git history graph |
|
||||
| `gpid` | processes | Give the list of PIDs matching the given process name(s) |
|
||||
| `gprune` | git | Delete local branches already merged into the main branch |
|
||||
| `greset` | git | Reset the current branch to upstream, stashing local changes first |
|
||||
| `groot` | git | Display the repository root path, or change directory to it with `-g` |
|
||||
| `gsync` | git | Fetch and rebase the current branch onto its upstream |
|
||||
| `gst` | git | Display compact git status with branch tracking information |
|
||||
| `gwip` | git | Create a quick WIP checkpoint commit |
|
||||
| `help` | help | Display the list of available functions and basic usage; `help <command>` delegates to `<command> --help` |
|
||||
| `isipv4` | net | Tell if the given parameter is a valid IPv4 address |
|
||||
| `isipv6` | net | Tell if the given parameter is a valid IPv6 address |
|
||||
| `ku` | processes | Kill all processes owned by the given user name or ID |
|
||||
| `matrix` | rain | Console screensaver with Matrix-style digital rain (binary, kana, ascii charset) |
|
||||
| `mcd` | filefct | Create a directory and immediately move into it |
|
||||
| `meteo` | info | Display weather forecast for the configured or given city |
|
||||
| `myextip` | net | Get information about your public IP address |
|
||||
| `get_pkgmgr` | packages | Detect the active package manager of the running distribution (`apt`, `dnf`, `yum`, `zypper`, `pacman`, `apk`, `portage`, `xbps`, `nix`) |
|
||||
| `pkgs` | packages | Search for a pattern in installed package names (distro-aware via `get_pkgmgr`, supports `-i`) |
|
||||
| `ppg` | processes | Look for the given pattern in running processes |
|
||||
| `ppn` | processes | List processes matching an exact command name |
|
||||
| `ppu` | processes | List processes owned by a specific user |
|
||||
| `profile_upgrade` | updates | Upgrade profile to the latest version (git pull or archive) |
|
||||
| `pwdscore` | pwd | Calculate the strength score of a given password |
|
||||
| `rain` | rain | Console screensaver with falling-rain effect (multiple color themes) |
|
||||
| `rmhost` | ssh | Remove host (name and IP) from SSH known_hosts; supports `--all-users` as root |
|
||||
| `rmspc` | filefct | Replace spaces in filenames with underscores (or a custom character) |
|
||||
| `setc` | lang | Set locale to standard C (POSIX) |
|
||||
| `setlocale` | lang | Set console locale to any installed locale |
|
||||
| `settrace` | debug | Activate or deactivate ERR trap to display backtrace on script errors |
|
||||
| `showinfo` | info | Display welcome banner and system information (figlet + neofetch/fastfetch) |
|
||||
| `ssr` | ssh | SSH into a server as root, forwarding extra ssh options |
|
||||
| `taz` | compress | Compress files and directories into a chosen archive format |
|
||||
| `urlencode` | net | URL-encode a string |
|
||||
| `utaz` | compress | Smartly uncompress archives (zip, tar.gz/bz2/xz/lz, rar, arj, lha, ace, 7z, zst, cpio, cab, deb, rpm) |
|
||||
| `ver` | info | Display the installed profile version |
|
||||
|
||||
Locale shortcut functions (`setfr`, `setus`, etc.) are dynamically generated at
|
||||
startup from the `SET_LOCALE` configuration key (see section 4).
|
||||
|
||||
### 3.3. Bash completion
|
||||
|
||||
profile loads all `*.sh` files found under `profile.d/bash-completion/`
|
||||
automatically in interactive sessions. This directory is the right place to add
|
||||
any custom completion definitions. profile already ships completions for its git
|
||||
helper functions there (`git-completion.sh`).
|
||||
|
||||
## 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.2](#22-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` | `auto` | Compression threads (`auto` = runtime CPU count, or explicit positive integer) |
|
||||
| `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` |
|
||||
|
||||
**`[git]`**
|
||||
|
||||
| Key | Default | Description |
|
||||
|---|---|---|
|
||||
| `GIT_MAIN_BRANCH` | `main` | Fallback main branch name used when remote HEAD cannot be detected |
|
||||
| `GIT_DEFAULT_REMOTE` | `origin` | Default remote used by git helper functions |
|
||||
| `GIT_WIP_PREFIX` | `wip` | Prefix used by `gwip` when generating automatic checkpoint messages |
|
||||
| `GIT_GACP_AUTO_ADD` | `1` | Set to `1` to make `gacp` automatically add all modified files when no explicit file list is given; set to `0` to require explicit paths or the `-a` flag |
|
||||
|
||||
**`[info]`**
|
||||
|
||||
| Key | Default | Description |
|
||||
|---|---|---|
|
||||
| `METEO_DEFAULT_CITY` | _(empty)_ | Default city for `meteo` when no argument is given |
|
||||
|
||||
**`[net]`**
|
||||
|
||||
| Key | Default | Description |
|
||||
|---|---|---|
|
||||
| `DWL_PREFERRED_TOOL` | _(empty)_ | Force `dwl` to use `curl`, `wget`, or `fetch` (auto-detected when unset) |
|
||||
| `MYEXTIP_DEFAULT_URL` | `https://ip-api.com/json` | API endpoint used by `myextip` |
|
||||
|
||||
**`[packages]`**
|
||||
|
||||
| Key | Default | Description |
|
||||
|---|---|---|
|
||||
| `PKGS_DEFAULT_IGNORE_CASE` | `0` | Set to `1` to make `pkgs` case-insensitive by default |
|
||||
|
||||
**`[processes]`**
|
||||
|
||||
| Key | Default | Description |
|
||||
|---|---|---|
|
||||
| `PPU_DEFAULT_FORMAT` | `pid,user,comm,args` | `ps` output format used by `ppu` |
|
||||
| `KU_DEFAULT_SIGNAL` | `TERM` | Default signal sent by `ku` |
|
||||
|
||||
**`[updates]`**
|
||||
|
||||
| Key | Default | Description |
|
||||
|---|---|---|
|
||||
| `UPDT_DEFAULT_BRANCH` | `master` | Git branch used for update checks and `profile_upgrade`. Changing this value causes `profile_upgrade` to automatically switch the local checkout to the new branch on the next run and display a warning. |
|
||||
|
||||
### 4.3. Locale shortcuts
|
||||
|
||||
The `[lang]` key `SET_LOCALE` accepts a comma-separated list of
|
||||
`alias:locale` pairs. Each pair generates a function of that name at startup:
|
||||
|
||||
```ini
|
||||
SET_LOCALE = fr:fr_FR.UTF-8, us:en_US.UTF-8
|
||||
```
|
||||
|
||||
This creates `setfr` and `setus`. Use `setlocale <locale>` to switch to any
|
||||
installed locale directly.
|
||||
|
||||
Set `DEFAULT_LANG` to one of the defined aliases to activate that locale
|
||||
automatically at login:
|
||||
|
||||
```ini
|
||||
DEFAULT_LANG = fr
|
||||
```
|
||||
|
||||
If `DEFAULT_LANG` is set but does not match any alias in `SET_LOCALE`, a
|
||||
warning is displayed and no locale change is applied.
|
||||
|
||||
### 4.4. Prompt theming
|
||||
|
||||
The prompt appearance is controlled by two mechanisms that are applied in order
|
||||
(later values win):
|
||||
|
||||
1. **Theme file** — sets a base colour palette.
|
||||
2. **`[prompt]` section** in `profile.conf` — per-key overrides on top of the theme.
|
||||
|
||||
**Selecting a theme:**
|
||||
|
||||
```ini
|
||||
[prompt]
|
||||
PROMPT_THEME = dark # bare name → profile.d/themes/dark.theme
|
||||
PROMPT_THEME_DIR = ~/.mythemes # optional: custom search directory
|
||||
```
|
||||
|
||||
Built-in themes: `default`, `dark`, `light`, `solarized`, `solarized-light`,
|
||||
`monokai`, `monochrome`, `abyss`, `plasma`, `adwaita`.
|
||||
|
||||
**Overriding individual prompt colour slots:**
|
||||
|
||||
```ini
|
||||
[prompt]
|
||||
PROMPT_COLOR_USER_FG = $ICyan
|
||||
PROMPT_COLOR_DIR_FG = $IYellow
|
||||
```
|
||||
|
||||
The eleven available `PROMPT_COLOR_*` keys are:
|
||||
|
||||
| Key | Role |
|
||||
|---|---|
|
||||
| `PROMPT_COLOR_TIME_FG` / `TIME_BG` | Clock foreground / background |
|
||||
| `PROMPT_COLOR_BAR_BG` | Main bar background |
|
||||
| `PROMPT_COLOR_OK_FG` / `OK_MARK` | Exit-code text / checkmark on success |
|
||||
| `PROMPT_COLOR_ERR_BG` / `ERR_FG` / `ERR_MARK` | Error bar background / text / X mark |
|
||||
| `PROMPT_COLOR_ROOT_FG` | Username colour when running as root |
|
||||
| `PROMPT_COLOR_USER_FG` | Username@host colour for normal users |
|
||||
| `PROMPT_COLOR_DIR_FG` | Working directory colour |
|
||||
|
||||
**Writing a custom theme file:**
|
||||
|
||||
Theme files live in `profile.d/themes/` and use the `.theme` extension. They
|
||||
are **parsed, not executed** — no shell code runs. Only two value forms are
|
||||
accepted:
|
||||
|
||||
```ini
|
||||
# Colour variable reference (resolved from profile.d/disp.sh)
|
||||
PROMPT_COLOR_DIR_FG = "$ICyan"
|
||||
|
||||
# Raw ANSI escape sequence (single block, 16-colour or 24-bit true-colour)
|
||||
PROMPT_COLOR_BAR_BG = "\e[48;2;7;54;66m"
|
||||
```
|
||||
|
||||
Any unknown key, unsafe value, or shell construct is discarded with a warning.
|
||||
Theme files may also override the standard colour variables (`Black`, `Blue`,
|
||||
`On_IBlack`, etc.) to remap the entire terminal palette used by `ls`, `grep`,
|
||||
and other colour-aware tools.
|
||||
|
||||
True-colour themes (`solarized`, `solarized-light`) require a terminal with
|
||||
24-bit colour support (Konsole, iTerm2, kitty, Alacritty, Windows Terminal).
|
||||
Verify support with:
|
||||
```bash
|
||||
printf '\e[38;2;38;139;210mTrue colour test\e[0m\n'
|
||||
```
|
||||
|
||||
## 5. Contact and more information
|
||||
### 5.1. New users
|
||||
If you use (or plan to use) `profile`, I'll be happy if you simply mail me to
|
||||
let me know, especially if you don't plan to contribute. If you plan to
|
||||
contribute, I'll be twice happier for sure!
|
||||
|
||||
### 5.2. Bugs
|
||||
**profile** bug tracker is hosted on its Gitea instance. Check the
|
||||
https://git.geoffray-levasseur.org/fatalerrors/profile page. If you find a bug,
|
||||
you can also submit a bug report to the maintainer mail address mentioned at
|
||||
the end of that document. A bug report may contain the command line parameters
|
||||
where the bug happens, OS details, the module that trigger it, if any, and the
|
||||
log file containing the error. Cygwin users: please note that bash
|
||||
implementation in Cygwin triggers regularly bugs on advanced code that triggers
|
||||
nothing with Linux or BSD. Please do not send synthax error bug repports if you
|
||||
didn't test the same code in the same conditions using a real Unix.
|
||||
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.
|
||||
|
||||
Please check the to-do list before sending any feature request, as it might
|
||||
have already be requested.
|
||||
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.
|
||||
|
||||
### 4.3. How to contribute?
|
||||
### 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.
|
||||
|
||||
It's heavily recommended to use git to obtain the latest copy of 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 init.sh.
|
||||
Read [CONTRIBUTING.md](./doc/CONTRIBUTING.md) for code style conventions,
|
||||
branch workflow, and how to submit a patch or pull request.
|
||||
|
||||
Code written in Python or Perl might be accepted as long as it's not mobilizing
|
||||
a lot of dependencies (forget big framework). Anything that need the
|
||||
installation of packages not provided in minimal Debian or CentOS installation
|
||||
will be probably rejected.
|
||||
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.
|
||||
|
||||
### 4.4. License, website, and maintainer
|
||||
Everything except configuration files is licensed under BSD-3 license. Please
|
||||
check license file allong this one.
|
||||
### 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).
|
||||
Note that this website is still under construction and needs some more care.
|
||||
|
||||
You can mail author to fatalerrors \<at\> geoffray-levasseur \<dot\> org.
|
||||
You can mail the author at fatalerrors \<at\> geoffray-levasseur \<dot\> org.
|
||||
|
||||
-----------------------------------------------------------------------------
|
||||
|
||||
Documentation (c) 2021-2022 Geoffray Levasseur.
|
||||
Documentation (c) 2021-2026 Geoffray Levasseur.
|
||||
|
||||
This file is distributed under3-clause BSD license. The complete license
|
||||
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
|
||||
|
||||
294
doc/CHANGELOG.md
Executable file
294
doc/CHANGELOG.md
Executable file
@@ -0,0 +1,294 @@
|
||||
# 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`).
|
||||
|
||||
---
|
||||
|
||||
## [4.1.0] — 2026-05-07
|
||||
|
||||
### Added
|
||||
- `profile.sh --install` command to automatically configure profile loading in
|
||||
shell startup files.
|
||||
- `--install --bashrc` and `--install --profile` target selectors for
|
||||
single-file installation.
|
||||
- **`git.sh`** — entirely new module providing git workflow helpers: `gst`,
|
||||
`ggraph`, `gsync`, `gacp`, `greset`, `gwip`, `gprune`, `groot`.
|
||||
- Dedicated git helper completions under `profile.d/bash-completion/`, loaded
|
||||
automatically in interactive sessions from `profile.d/bash-completion/*.sh`.
|
||||
|
||||
### Changed
|
||||
- `disp` now wraps long messages on terminal width, avoids mid-word splits, and
|
||||
aligns continuation lines with the message body after the prefix.
|
||||
- `help` now supports `help <command>` and delegates to `<command> --help`.
|
||||
- `taz` now supports `-p auto` / `--parallel=auto` to automatically use the
|
||||
runtime CPU count. This mode is now the default via
|
||||
`TAZ_DEFAULT_THREADS=auto`.
|
||||
- `taz` keeps backward compatibility with legacy `TAZ_DEFAULT_THREADS=0`
|
||||
values by interpreting `0` as `auto`.
|
||||
|
||||
### Fixed
|
||||
- Startup responsiveness improved: `check_updates -q` now uses a short network
|
||||
timeout so unavailable/slow networks no longer delay prompt readiness.
|
||||
- `dwl` gained timeout support (`-t` / `--timeout`) and is now used by quiet
|
||||
startup update checks to enforce fast failure.
|
||||
- `profile.sh` now detects direct execution and warns that it is designed to be
|
||||
sourced.
|
||||
|
||||
## [4.0.0] — 2026-04-23
|
||||
|
||||
### Added
|
||||
- New `profile.conf` reference template at `doc/profile.conf.example`.
|
||||
- Dynamic locale shortcuts generated from `SET_LOCALE` and startup default
|
||||
language selection through `DEFAULT_LANG`.
|
||||
- Prompt theming system with bundled themes (`default`, `dark`, `light`,
|
||||
`solarized`, `solarized-light`, `monokai`, `monochrome`, `abyss`, `plasma`,
|
||||
`adwaita`) and per-key prompt color overrides.
|
||||
- Module defaults exposed as configuration keys in `profile.conf`.
|
||||
|
||||
### Changed
|
||||
- `utaz` now supports a wider range of archive formats.
|
||||
- Prompt and theme rendering improved, including better 24-bit color support.
|
||||
- Overall code quality and maintainability improved across modules.
|
||||
|
||||
### Documentation
|
||||
- README updated with full function reference and configuration tables.
|
||||
- New and expanded docs in `doc/` (`CONTRIBUTING.md`, `FAQ.md`, `todo.md`).
|
||||
- Historical releases imported from `history.txt` into this changelog.
|
||||
|
||||
---
|
||||
|
||||
## [3.99.2-4_rc_2] — 2026-04-21
|
||||
|
||||
### Fixed
|
||||
- **`prompt.sh`** — `\$Last_Command` in PS1 was escaped, preventing the exit
|
||||
code from ever appearing in the prompt (the local variable no longer exists
|
||||
when PS1 is rendered by bash). Removed the backslash so the value is embedded
|
||||
at `set_prompt` build time.
|
||||
- **`filefct.sh` — `file_stats()`** — a stray unconditional `shift` after
|
||||
`esac` doubled-shifted arguments already shifted by each `case` branch;
|
||||
successive options such as `-H -d` were silently skipped.
|
||||
- **`packages.sh` — `pkgs()`** — replaced the unreliable binary-presence test
|
||||
(`command -v dpkg / rpm`) with the new `get_pkgmgr` function. Also corrected
|
||||
a typo in the "no package manager" error message (`avialable` → `available`).
|
||||
- **`processes.sh` — `kt()`** — copy-paste error: usage error message read
|
||||
`"Usage: ppg <string>"` instead of `"Usage: kt <pid>"`.
|
||||
|
||||
### Added
|
||||
- **`packages.sh` — `get_pkgmgr()`** — new exported helper that detects the
|
||||
active package manager of the running distribution. Detection first reads
|
||||
`/etc/os-release` (`ID` then `ID_LIKE`), then falls back to a
|
||||
fixed-priority binary scan. Supported families: `apt`, `dnf`, `yum`,
|
||||
`zypper`, `pacman`, `apk`, `portage`, `xbps`, `nix`. Returns 1 when
|
||||
nothing is identified. Available to all future commands in `packages.sh`.
|
||||
|
||||
---
|
||||
|
||||
## [3.99.1-4_rc_1] — 2026
|
||||
|
||||
### Added
|
||||
- **Theming system** — `load_theme` in `profile.d/prompt.sh` loads `.theme`
|
||||
files from `profile.d/themes/` (or a custom directory set via
|
||||
`PROMPT_THEME_DIR`). Theme files are **parsed, not executed** — no shell code
|
||||
ever runs from a theme.
|
||||
- **Built-in themes** — `default`, `dark`, `light`, `solarized` (24-bit
|
||||
true-colour), `solarized-light` (24-bit), `monokai`, `monochrome`, `abyss`,
|
||||
`plasma`, `adwaita`.
|
||||
- **`[prompt]` section** — `PROMPT_THEME` and `PROMPT_THEME_DIR` keys to select
|
||||
a theme; eleven `PROMPT_COLOR_*` keys for per-key overrides in `profile.conf`.
|
||||
- **Standard colour variables as theme keys** — theme files may also override
|
||||
the `disp.sh` colour variables (`Black`, `Blue`, `On_IBlack`, …) to remap the
|
||||
terminal palette used by `ls`, `grep`, and colour-aware tools.
|
||||
- **Module configuration defaults** — all modules now expose their hardcoded
|
||||
defaults as `profile.conf` keys: `TAZ_*`, `UTAZ_*`, `EXPANDLIST_*`,
|
||||
`CLEAN_*`, `RMSPC_*`, `FINDBIG_*`, `RAIN_*`, `MATRIX_*`, `SSH_DEFAULT_OPT`,
|
||||
`GENPWD_*`, `PWDSCORE_*`, `BUSY_*`, `METEO_DEFAULT_CITY`, `DWL_*`,
|
||||
`MYEXTIP_*`, `PKGS_*`, `PPU_*`, `KU_*`, `UPDT_DEFAULT_BRANCH`.
|
||||
- **`UPDT_DEFAULT_BRANCH`** — configures the git branch used by `check_updates`
|
||||
and `profile_upgrade`. Changing the key causes `profile_upgrade` to
|
||||
automatically switch the local checkout to the new branch and display a
|
||||
warning.
|
||||
- **`doc/` directory** — `CONTRIBUTING.md`, `FAQ.md`, `profile.conf.example`
|
||||
(annotated template), `todo.md`.
|
||||
- **`.gitignore`** — `profile.conf` is now git-ignored so personal settings are
|
||||
never accidentally staged.
|
||||
|
||||
### Changed
|
||||
- README §2 now explains how to create `profile.conf` from
|
||||
`doc/profile.conf.example` (new section 2.1 "Initial configuration").
|
||||
- README §4 updated with full module-defaults tables, theming reference, and a
|
||||
note about `profile.conf` being git-ignored.
|
||||
- Theme values no longer carry the `export` keyword (they are not shell
|
||||
variables, only data).
|
||||
|
||||
### Security
|
||||
- `load_theme` uses a strict allowlist (no `eval`, no sourcing). Only
|
||||
`PROMPT_COLOR_*` keys and known `disp.sh` colour variable names are accepted.
|
||||
Values must match `\$[A-Za-z_][A-Za-z0-9_]*` or `\\e\[[0-9;]*m`; any other
|
||||
value is discarded with a warning.
|
||||
|
||||
---
|
||||
|
||||
## [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.
|
||||
|
||||
---
|
||||
|
||||
> **Note:** The section below was imported from `history.txt` to preserve
|
||||
> pre-`3.95.x-4_beta` release notes.
|
||||
|
||||
## Legacy releases (imported from `history.txt`)
|
||||
|
||||
### [3.6.1] — 2026-03-05
|
||||
- Fix typo in `compress.sh`.
|
||||
|
||||
### [3.6.0] — 2026-03-05
|
||||
- Improved `utaz` with broader multi-format support.
|
||||
- Introduced `ppu` and `ppn`.
|
||||
- Improved update system.
|
||||
|
||||
### [3.5.0] — 2026-03-04
|
||||
- `rain` now has configurable speed and color.
|
||||
- `showinfo` adapted to `fastfetch` (in addition to `neofetch`).
|
||||
|
||||
### [3.3.1] — 2022-02-24
|
||||
- Fixed version detection.
|
||||
- Added `busy`.
|
||||
- Fixed use of library functions before loading.
|
||||
|
||||
### [3.3.0] — 2022-11-28
|
||||
- Initial version update support.
|
||||
- Changed versioning code.
|
||||
- Added installation path detection.
|
||||
|
||||
### [3.2.3] — 2022-11-28
|
||||
- Improved README.
|
||||
|
||||
### [3.2.2] — 2022-11-21
|
||||
- Fixed `taz` compression level parsing.
|
||||
- Fixed typo in `dpkgs`.
|
||||
|
||||
### [3.2.1] — 2022-11-20
|
||||
- Fixed several messages.
|
||||
- Made `dpkgs` RPM-aware (initial support).
|
||||
- Removed version history from main script and reverted declaration order.
|
||||
- Added required license information in all files.
|
||||
- Completed `LICENSE` file.
|
||||
|
||||
### [3.2.0] — 2022-11-18
|
||||
- Created `disp` command and integrated it across the codebase.
|
||||
|
||||
### [3.1.1] — 2022-11-10
|
||||
- `genpwd`: added feasibility check for requested password constraints.
|
||||
|
||||
### [3.1.0] — 2022-11-08
|
||||
- Added password generator.
|
||||
|
||||
### [3.0.1] — 2022-11-07
|
||||
- Added concatenation option to `rmspc`.
|
||||
- Added `ku`.
|
||||
- Improved error handling in `meteo`.
|
||||
|
||||
### [3.0.0] — 2022-08-27
|
||||
- Split code into several files/modules.
|
||||
- Added `rain` screensaver.
|
||||
|
||||
### [2.8.2] — 2022-07-29
|
||||
- Added warning for non-bash users.
|
||||
|
||||
### [2.8.1] — 2022-07-19
|
||||
- Cleanup, fixes and optimizations.
|
||||
|
||||
### [2.8.0] — 2022-06-24
|
||||
- Added `backtrace`, `error` and `settrace`.
|
||||
- Bugfixes in `showinfo`.
|
||||
|
||||
### [2.7.1] — 2022-06-22
|
||||
- Minor corrections.
|
||||
- Added `help` command.
|
||||
|
||||
### [2.7.0] — 2022-06-21
|
||||
- Added `isipv4` and `isipv6`, integrated into `rmhost`.
|
||||
- Removed broken Konsole save/restore support.
|
||||
|
||||
### [2.6.3] — 2021-10-18
|
||||
- Changed PS1 to status-bar style.
|
||||
- Minor improvements.
|
||||
|
||||
### [2.6.2] — 2021-02-26
|
||||
- Bugfix in `taz` for directories with trailing slash.
|
||||
|
||||
### [2.6.1] — 2020-12-25
|
||||
- Added checks in `rmhost`.
|
||||
- Improved `rmspc`.
|
||||
- Created `expandlist`.
|
||||
|
||||
### [2.6.0] — 2020-10-24
|
||||
- Added Konsole session save/restore.
|
||||
|
||||
### [2.5.3] — 2020-09-11
|
||||
- Added aliases, improved code consistency and fixed typos.
|
||||
- Improved `utaz`, removed `showdiskmap`, removed remaining French text.
|
||||
- Added license information for future publication.
|
||||
|
||||
### [2.5.2] — 2020-03-06
|
||||
- Sorted and improved aliases.
|
||||
|
||||
### [2.5.1] — 2020-03-05
|
||||
- Language consistency fixes.
|
||||
- Added `pigz` support in `taz`.
|
||||
|
||||
### [2.5.0] — 2020-03-03
|
||||
- Added `taz` and `rmspc`.
|
||||
- Renamed `auzip` to `utaz` and improved it.
|
||||
|
||||
### [2.4.0] — 2020-03-02
|
||||
- Added `auzip`.
|
||||
|
||||
### [2.3.2] — 2020-01-31
|
||||
- `figlet`: changed default font to `ansi_shadow`.
|
||||
|
||||
### [2.3.1] — 2020-01-16
|
||||
- Bugfix: non-interactive shells were blocked by some functions.
|
||||
|
||||
### [2.3.0] — 2020-01-08
|
||||
- Added `figlet` and `neofetch` as MOTD replacement.
|
||||
|
||||
### [2.2.0] — 2019-12-16
|
||||
- Added `showinfo`.
|
||||
- First implementation of `showdiskmap`.
|
||||
|
||||
### [2.1.2] — 2019-09-24
|
||||
- Bugfix in profile version display.
|
||||
|
||||
### [2.1.1] — 2019-09-23
|
||||
- Bugfix in `dpkgs`.
|
||||
|
||||
### [2.1.0] — 2018-09-16
|
||||
- Added `rmhost`, `setc`, `setfr`.
|
||||
- Improved locale management.
|
||||
|
||||
### [2.0.1] — 2017-02-04
|
||||
- `clean` improvements (`--shell`).
|
||||
|
||||
### [2.0.0] — 2015-10-24
|
||||
- Added advanced functions (`clean`, `ssr`, etc.).
|
||||
|
||||
### [1.0.0] — 2013-02-16
|
||||
- Initial version.
|
||||
|
||||
### [Initial fork]
|
||||
Forked default Bash profile from Beyond Linux From Scratch by
|
||||
* James Robertson <jameswrobertson@earthlink.net>
|
||||
* Dagmar d'Surreal <rivyqntzne@pbzpnfg.arg>
|
||||
210
doc/CONTRIBUTING.md
Executable file
210
doc/CONTRIBUTING.md
Executable file
@@ -0,0 +1,210 @@
|
||||
# Contributing to profile
|
||||
|
||||
Thank you for your interest in contributing. This document explains how to get
|
||||
set up, what the conventions are, and how to submit work.
|
||||
|
||||
---
|
||||
|
||||
## 1. Before you start
|
||||
|
||||
- Check the [to-do list](./todo.md) to see if your idea is already planned.
|
||||
- Check the [issue tracker](https://git.geoffray-levasseur.org/fatalerrors/profile/issues)
|
||||
to avoid duplicate work.
|
||||
- For significant changes, open an issue or contact the maintainer before
|
||||
writing code — alignment on design saves everyone time.
|
||||
|
||||
---
|
||||
|
||||
## 2. Getting the source
|
||||
|
||||
A Git clone is mandatory for contributions:
|
||||
|
||||
```bash
|
||||
git clone https://git.geoffray-levasseur.org/fatalerrors/profile.git
|
||||
cd profile
|
||||
```
|
||||
|
||||
Always work from the **latest commit on `master`** (or the branch you intend
|
||||
to target). Stale forks cause avoidable merge conflicts.
|
||||
|
||||
---
|
||||
|
||||
## 3. Branch policy
|
||||
|
||||
| Branch | Purpose |
|
||||
|---|---|
|
||||
| `master` | Main development branch — new features and enhancements go here |
|
||||
| `<version>` (e.g. `3.x`) | Maintenance branch for a released version — bugfixes backported from `master` |
|
||||
|
||||
**New functionality** must always target `master`.
|
||||
|
||||
**Bugfixes** must target the branch where the bug was introduced:
|
||||
- If the bug exists in a released version, open the fix against that version's
|
||||
maintenance branch first, then cherry-pick onto `master`.
|
||||
- If the bug is only in `master` (unreleased), fix it directly on `master`.
|
||||
- During a release-candidate cycle, bugfixes go on the `x.*` branch and are
|
||||
merged back into `master` before the final release.
|
||||
|
||||
Do **not** add new features to a maintenance branch.
|
||||
|
||||
---
|
||||
|
||||
## 4. Versioning scheme
|
||||
|
||||
Versions follow the format **`MAJOR.MINOR.PATCH`** where the `MINOR` number
|
||||
conveys the development stage of the next major release:
|
||||
|
||||
| Minor range | Stage | Rules |
|
||||
|---|---|---|
|
||||
| `x.90.y` | **Alpha** toward `x+1` | Stays on `master`. Development is open: new features are welcome, regressions are acceptable. |
|
||||
| `x.95.y` | **Beta** toward `x+1` | The `x+1.*` maintenance branch is created at this point. No regression unless absolutely necessary; new features still allowed. |
|
||||
| `x.99.y` | **RC** toward `x+1` | Bugfixes only. No new features. No regression allowed. Becomes `x+1.0.0` when stable. |
|
||||
|
||||
Examples: `3.90.1` is the first alpha toward `4.0`, `3.99.2` is the second
|
||||
release candidate for `4.0`.
|
||||
|
||||
The `PATCH` number increments freely within a stage. A bump in `MINOR`
|
||||
(e.g. `90` → `95`) always indicates a stage promotion in development phase.
|
||||
|
||||
Any experimental version must have it's dedicated branch.
|
||||
|
||||
---
|
||||
|
||||
## 5. Development environment
|
||||
|---|---|---|
|
||||
| Bash | 4.3 | Namerefs (`local -n`) required |
|
||||
| shellcheck | any recent | Run before every commit |
|
||||
| git | any | For contributing patches |
|
||||
| bats-core | 1.x | Optional — for running the test suite |
|
||||
|
||||
Install shellcheck:
|
||||
```bash
|
||||
# Debian / Ubuntu
|
||||
apt-get install shellcheck
|
||||
|
||||
# Fedora / RHEL
|
||||
dnf install ShellCheck
|
||||
|
||||
# macOS
|
||||
brew install shellcheck
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Code style
|
||||
|
||||
### General rules
|
||||
- **Bash only** — no external interpreters in core modules. Python or Perl is
|
||||
acceptable for completely self-contained, optional utilities that have no
|
||||
dependencies beyond a minimal Debian or CentOS installation.
|
||||
- **4-space indentation** — no tabs.
|
||||
- **`[[ … ]]`** for all conditionals — not `[ … ]`.
|
||||
- **`(( … ))`** for arithmetic — not `$(( … ))` in conditionals.
|
||||
- **`local`** for all function-internal variables — avoid polluting the
|
||||
environment. Prefer upper case for global and lowercase for local.
|
||||
- **`printf`** instead of `echo` all the time.
|
||||
- **Never `eval`** — use namerefs (`local -n`), `${!varname}` indirection, or
|
||||
`declare -g` instead.
|
||||
- **No hardcoded defaults** — wire every configurable value through
|
||||
`${VAR:-default}` and document the key in `profile.conf` and `README.md §4`.
|
||||
|
||||
### Function conventions
|
||||
- Public functions **must** be exported: `export -f funcname`.
|
||||
- Every public function **must** support `-h` / `--help` and print usage to
|
||||
stdout, returning 0.
|
||||
- Use `getopt` (not `getopts`) for option parsing — it handles long options and
|
||||
`--` correctly.
|
||||
- Follow existing error-return conventions: 0 = success, 1 = usage error,
|
||||
2 = bad options, 3 = missing dependency, 4+ = runtime failure.
|
||||
- Prefix all local helper variables with a short unique prefix (e.g. `_taz_`)
|
||||
to prevent collisions with caller-scope variables.
|
||||
|
||||
### Module structure
|
||||
Every new module should follow this pattern:
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# <copyright block identical to existing modules>
|
||||
|
||||
load_conf "<module_name>"
|
||||
|
||||
# --- functions ---
|
||||
|
||||
export -f my_function
|
||||
|
||||
# EOF
|
||||
```
|
||||
|
||||
Add the `load_conf` call near the top after any variable declarations.
|
||||
|
||||
---
|
||||
|
||||
## 7. Configuration keys
|
||||
|
||||
When adding a configurable default:
|
||||
|
||||
1. Use `${MY_VAR:-hardcoded_default}` in the function body.
|
||||
2. Add a commented-out entry with a description to `profile.conf`.
|
||||
3. Document the key in the matching table in `README.md §4.2`.
|
||||
|
||||
---
|
||||
|
||||
## 8. Theming
|
||||
|
||||
New theme files go in `profile.d/themes/` with a `.theme` extension.
|
||||
They are **parsed, not executed** — do not add shell logic.
|
||||
See the existing themes and `README.md §4.4` for the allowed syntax.
|
||||
|
||||
---
|
||||
|
||||
## 9. Running shellcheck
|
||||
|
||||
```bash
|
||||
shellcheck -x profile.sh profile.d/*.sh
|
||||
```
|
||||
|
||||
All warnings must be resolved before a patch will be accepted. Accepted
|
||||
suppression directives (`# shellcheck disable=SCxxxx`) require an inline
|
||||
comment explaining why the suppression is necessary.
|
||||
|
||||
---
|
||||
|
||||
## 10. Submitting a contribution
|
||||
|
||||
### Via Git (preferred)
|
||||
1. Contact the maintainer to obtain push access, or fork on the Gitea instance.
|
||||
2. Create a branch: `git checkout -b feature/my-feature`.
|
||||
3. Commit with a clear subject line: `module: short description (≤ 72 chars)`.
|
||||
4. Push and open a pull request against `master`.
|
||||
|
||||
### Via patch
|
||||
If you do not have push access:
|
||||
```bash
|
||||
git format-patch origin/master
|
||||
```
|
||||
Send the resulting `.patch` file(s) to
|
||||
`fatalerrors <at> geoffray-levasseur <dot> org`.
|
||||
|
||||
### Commit message format
|
||||
```
|
||||
module: imperative short description
|
||||
|
||||
Optional longer explanation of what changed and why. Wrap at 72 characters.
|
||||
Reference issue numbers if applicable: closes #42.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. What will be rejected
|
||||
|
||||
- Code requiring packages not in a minimal Debian or CentOS install.
|
||||
- Use of `eval`, `source`-based config loading, or other code-injection vectors.
|
||||
- Changes that break Bash 4.3 compatibility.
|
||||
- Patches without a passing `shellcheck` run.
|
||||
- New functions without `--help` support.
|
||||
|
||||
---
|
||||
|
||||
## 12. Financial contributions
|
||||
|
||||
Contact the maintainer by mail if you wish to make a financial contribution.
|
||||
347
doc/FAQ.md
Executable file
347
doc/FAQ.md
Executable file
@@ -0,0 +1,347 @@
|
||||
# Frequently Asked Questions
|
||||
|
||||
---
|
||||
|
||||
## Installation & loading
|
||||
|
||||
**Q: How do I install profile automatically into my shell startup files?**
|
||||
|
||||
Run the installer directly (no need to source first):
|
||||
```bash
|
||||
bash <installpath>/profile/profile.sh --install
|
||||
```
|
||||
This appends the required `source` line to both `~/.bashrc` and `~/.profile`.
|
||||
To target only one file:
|
||||
```bash
|
||||
bash <installpath>/profile/profile.sh --install --bashrc
|
||||
bash <installpath>/profile/profile.sh --install --profile
|
||||
```
|
||||
The operation is idempotent — running it again will not add a duplicate line.
|
||||
|
||||
---
|
||||
|
||||
**Q: I ran `profile.sh` directly and got a warning about sourcing.**
|
||||
|
||||
profile.sh is designed to be *sourced*, not executed:
|
||||
```bash
|
||||
source <installpath>/profile/profile.sh
|
||||
```
|
||||
The only exception is `--install`, which must be passed to a direct execution
|
||||
(`bash profile.sh --install`) to set up the sourcing line automatically.
|
||||
|
||||
---
|
||||
|
||||
**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: Can I use profile functions in scripts?**
|
||||
|
||||
Yes. The supported way is to source `profile.sh` from a Bash script:
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
source /path/to/profile/profile.sh
|
||||
|
||||
taz -p auto -f lz mydir
|
||||
```
|
||||
You can also source one module directly (for example
|
||||
`profile.d/compress.sh`) if you only need a subset of functions.
|
||||
|
||||
When you source a module directly, profile configuration parsing/loading from
|
||||
`profile.sh` is skipped, so defaults from `profile.conf` are not applied unless
|
||||
your script loads them explicitly.
|
||||
|
||||
`profile.sh` also detects whether the current shell is interactive. In
|
||||
non-interactive shells (typical script execution), interactive-only features
|
||||
are intentionally disabled: prompt setup, aliases, welcome/info messages, and
|
||||
startup update checks are not enabled.
|
||||
|
||||
In all cases, avoid aliases in scripts. Use real commands/functions instead,
|
||||
because alias expansion is interactive-shell oriented and can be disabled or
|
||||
behave differently in non-interactive execution.
|
||||
|
||||
---
|
||||
|
||||
**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`, but you can create your
|
||||
own theme.
|
||||
|
||||
---
|
||||
|
||||
**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`).
|
||||
|
||||
---
|
||||
|
||||
## Git helpers
|
||||
|
||||
**Q: What git helper functions does profile provide?**
|
||||
|
||||
All git helpers are defined in `profile.d/git.sh` (new in 4.1.0):
|
||||
|
||||
| Command | Purpose |
|
||||
|---|---|
|
||||
| `gst` | Compact status with branch tracking info |
|
||||
| `ggraph` | Decorated history graph |
|
||||
| `gsync` | Fetch and rebase onto upstream |
|
||||
| `gacp` | Add, commit and push in one command |
|
||||
| `greset` | Reset to upstream, stashing local changes first |
|
||||
| `gwip` | Quick WIP checkpoint commit |
|
||||
| `gprune` | Delete merged local branches |
|
||||
| `groot` | Print or cd to repository root |
|
||||
|
||||
All commands accept `-h` / `--help`.
|
||||
|
||||
---
|
||||
|
||||
**Q: Tab completion for `gacp` does not show modified files.**
|
||||
|
||||
Profile ships dedicated completions in `profile.d/bash-completion/git-completion.sh`,
|
||||
loaded automatically in interactive sessions. If completions are missing,
|
||||
check that the system git completion is installed:
|
||||
```bash
|
||||
# Debian / Ubuntu
|
||||
apt-get install bash-completion
|
||||
# Fedora / RHEL
|
||||
dnf install bash-completion
|
||||
```
|
||||
When the native git completion helpers are available, `gacp` path completion
|
||||
behaves exactly like `git add` (modified files, untracked files, directories).
|
||||
|
||||
---
|
||||
|
||||
**Q: `gacp` says "No files specified" even though I passed `-a`.**
|
||||
|
||||
The `-a` / `--auto` flag adds all modified files (equivalent to `git add -A`).
|
||||
The default depends on `GIT_GACP_AUTO_ADD` in `profile.conf`:
|
||||
```ini
|
||||
[git]
|
||||
GIT_GACP_AUTO_ADD = 1
|
||||
```
|
||||
Set to `1` to make `-a` the default.
|
||||
|
||||
---
|
||||
|
||||
## 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: How do I limit how long `dwl` waits for a download?**
|
||||
|
||||
Use the `-t` / `--timeout` option:
|
||||
```bash
|
||||
dwl -t 5 https://example.com/file.txt /tmp/file.txt
|
||||
```
|
||||
This sets a 5-second cap on both the connection and the overall transfer.
|
||||
The timeout is propagated to `curl` (`--max-time` + `--connect-timeout`),
|
||||
`wget` (`--timeout`), or `fetch` (`-T`) transparently.
|
||||
|
||||
---
|
||||
|
||||
**Q: The prompt takes a long time to appear when my network is unavailable.**
|
||||
|
||||
Fixed in 4.1.0. `check_updates -q` (called at startup) now enforces a
|
||||
3-second network timeout. If the update server is unreachable the check
|
||||
fails silently and the prompt appears immediately.
|
||||
|
||||
---
|
||||
|
||||
**Q: `help` only shows a list of functions. Can I get usage for a specific one?**
|
||||
|
||||
Yes — pass the command name as an argument:
|
||||
```bash
|
||||
help gacp
|
||||
help dwl
|
||||
help taz
|
||||
```
|
||||
This calls `<command> --help` and prints the full usage for that function.
|
||||
|
||||
---
|
||||
|
||||
**Q: `pkgs` does not find packages I know are installed.**
|
||||
|
||||
`pkgs` uses `get_pkgmgr` to detect the active package manager and delegates
|
||||
to the appropriate tool. Supported families: `apt` (Debian/Ubuntu),
|
||||
`dnf` / `yum` (RHEL/Fedora), `zypper` (openSUSE), `pacman` (Arch),
|
||||
`apk` (Alpine), `portage` (Gentoo), `xbps` (Void), `nix`, `brew` (macOS).
|
||||
If your distribution is not detected, run `get_pkgmgr` to see what is
|
||||
identified, and check that the package manager binary is in your `PATH`.
|
||||
|
||||
---
|
||||
|
||||
**Q: `profile_upgrade` says "no update available" but I know there is one.**
|
||||
|
||||
`check_updates` compares the content of the remote `version` file against
|
||||
`$PROFVERSION`. If `UPDT_DEFAULT_BRANCH` in `[updates]` points to a different
|
||||
branch than your installation, the version files may not match. Check:
|
||||
```bash
|
||||
cat "$MYPATH/version"
|
||||
```
|
||||
and make sure `UPDT_DEFAULT_BRANCH` matches the branch you track.
|
||||
|
||||
---
|
||||
|
||||
## Compatibility
|
||||
|
||||
**Q: Some functions misbehave on macOS / Cygwin.**
|
||||
|
||||
Both environments ship non-GNU userland utilities with different flags and
|
||||
behaviour. profile is primarily developed and tested on Linux (Debian and
|
||||
RHEL families). macOS and Cygwin bugs are low priority; patches that add
|
||||
compatibility without breaking Linux support are welcome.
|
||||
|
||||
---
|
||||
|
||||
**Q: Can I use profile with ZSH?**
|
||||
|
||||
Not officially. Blockers include `local -A` (ZSH requires `typeset -A`) and
|
||||
`local -n` namerefs. A compatibility layer is listed in `doc/todo.md` but
|
||||
has not been implemented yet.
|
||||
|
||||
---
|
||||
|
||||
## Miscellaneous
|
||||
|
||||
**Q: How do I completely disable profile for one session?**
|
||||
|
||||
```bash
|
||||
PROFILE_DISABLED=1 bash --norc
|
||||
```
|
||||
Or simply open a shell without sourcing `~/.bashrc` (`bash --norc`).
|
||||
|
||||
---
|
||||
|
||||
**Q: How do I report a bug?**
|
||||
|
||||
Open an issue on the
|
||||
[Gitea tracker](https://git.geoffray-levasseur.org/fatalerrors/profile/issues)
|
||||
or send a mail to `fatalerrors <at> geoffray-levasseur <dot> org` with:
|
||||
- The exact command that triggered the bug
|
||||
- Your OS and Bash version (`bash --version`)
|
||||
- The module involved
|
||||
- Any relevant error output
|
||||
29
doc/LICENSE
Executable file
29
doc/LICENSE
Executable file
@@ -0,0 +1,29 @@
|
||||
Copyright 2021-2026 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||
|
||||
This software is distributed under the BSD-3-Clause license with the
|
||||
following terms and conditions:
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
269
doc/profile.conf.example
Executable file
269
doc/profile.conf.example
Executable file
@@ -0,0 +1,269 @@
|
||||
# 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.
|
||||
# auto — detect CPU count at runtime (default)
|
||||
# N — explicit positive integer
|
||||
#TAZ_DEFAULT_THREADS=auto
|
||||
|
||||
# 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
|
||||
|
||||
# ==============================================================================
|
||||
[git]
|
||||
# Fallback main branch name used when remote HEAD cannot be detected.
|
||||
#GIT_MAIN_BRANCH=main
|
||||
|
||||
# Default remote used by git helper functions.
|
||||
#GIT_DEFAULT_REMOTE=origin
|
||||
|
||||
# Prefix used by gwip when generating automatic checkpoint messages.
|
||||
#GIT_WIP_PREFIX=wip
|
||||
|
||||
# gacp: Automatically add all modified files (git add -A) when no explicit file
|
||||
# list is provided. Set to 0 to require explicit file paths or the -a flag.
|
||||
#GIT_GACP_AUTO_ADD=1
|
||||
|
||||
# ==============================================================================
|
||||
[info]
|
||||
# meteo: Default city when no argument is given. Leave unset to require an
|
||||
# explicit city argument every time.
|
||||
#METEO_DEFAULT_CITY=Paris
|
||||
|
||||
# ==============================================================================
|
||||
[lang]
|
||||
# Comma-separated alias:locale pairs. One function is generated per entry.
|
||||
# Example: SET_LOCALE="fr:fr_FR.UTF-8,us:en_US.UTF-8,es:es_ES.UTF-8"
|
||||
# creates setfr, setus, setes.
|
||||
#SET_LOCALE=fr:fr_FR.UTF-8,us:en_US.UTF-8
|
||||
|
||||
# Alias to activate at login. Must match one of the aliases defined in SET_LOCALE above.
|
||||
# Example: DEFAULT_LANG=fr → calls setfr at startup.
|
||||
# Leave unset to keep the system default locale.
|
||||
#DEFAULT_LANG=fr
|
||||
|
||||
# ==============================================================================
|
||||
[net]
|
||||
# dwl: Force a specific download tool (curl, wget, fetch).
|
||||
# Unset = auto-detect (curl preferred, then wget, then fetch).
|
||||
#DWL_PREFERRED_TOOL=curl
|
||||
|
||||
# myextip: API endpoint for external IP lookup.
|
||||
# Alternatives: https://ipinfo.io/json, https://ip-api.com/json/
|
||||
#MYEXTIP_DEFAULT_URL=https://ip-api.com/json/
|
||||
|
||||
# ==============================================================================
|
||||
[packages]
|
||||
# pkgs: Case-insensitive search by default (0=no, 1=yes).
|
||||
#PKGS_DEFAULT_IGNORE_CASE=0
|
||||
|
||||
# ==============================================================================
|
||||
[processes]
|
||||
# ppu: ps output columns (comma-separated ps field names).
|
||||
#PPU_DEFAULT_FORMAT=pid,user,%cpu,%mem,start,time,command
|
||||
|
||||
# ku: Signal sent when killing a user's processes (without SIG prefix).
|
||||
#KU_DEFAULT_SIGNAL=TERM
|
||||
|
||||
# ==============================================================================
|
||||
[prompt]
|
||||
# Name of the prompt theme to load, or an explicit path to a .theme file.
|
||||
# Built-in themes: default, dark, light, solarized, solarized-light,
|
||||
# monokai, monochrome, abyss, plasma, adwaita
|
||||
# Solarized variants require a terminal with 24-bit true-colour support.
|
||||
#PROMPT_THEME=default
|
||||
|
||||
# Directory searched for bare theme names. Defaults to profile.d/themes/.
|
||||
#PROMPT_THEME_DIR=/path/to/my/themes
|
||||
|
||||
# Individual colour overrides — these always win over the loaded theme.
|
||||
# Values must be colour variable names from disp.sh (e.g. $Blue, $On_IBlack)
|
||||
# or raw ANSI escape sequences (e.g. \e[0;34m).
|
||||
#
|
||||
# Clock segment
|
||||
#PROMPT_COLOR_TIME_FG=$Blue
|
||||
#PROMPT_COLOR_TIME_BG=$On_IBlack
|
||||
#
|
||||
# Main bar background
|
||||
#PROMPT_COLOR_BAR_BG=$On_Blue
|
||||
#
|
||||
# Exit-code segment — success
|
||||
#PROMPT_COLOR_OK_FG=$White
|
||||
#PROMPT_COLOR_OK_MARK=$Green
|
||||
#
|
||||
# Exit-code segment — failure
|
||||
#PROMPT_COLOR_ERR_BG=$On_Red
|
||||
#PROMPT_COLOR_ERR_FG=$White
|
||||
#PROMPT_COLOR_ERR_MARK=$BYellow
|
||||
#
|
||||
# User / host
|
||||
#PROMPT_COLOR_ROOT_FG=$Red
|
||||
#PROMPT_COLOR_USER_FG=$Green
|
||||
#
|
||||
# Working directory
|
||||
#PROMPT_COLOR_DIR_FG=$ICyan
|
||||
|
||||
# ==============================================================================
|
||||
[pwd]
|
||||
# genpwd: Default password length.
|
||||
#GENPWD_DEFAULT_LENGTH=16
|
||||
|
||||
# genpwd: Maximum occurrences of any single character.
|
||||
#GENPWD_DEFAULT_OCCURS=2
|
||||
|
||||
# genpwd: Number of passwords generated per invocation.
|
||||
#GENPWD_DEFAULT_COUNT=1
|
||||
|
||||
# genpwd: Include symbols (1=yes, 0=no).
|
||||
#GENPWD_DEFAULT_SYMBOLS=1
|
||||
|
||||
# genpwd: Include uppercase letters (1=yes, 0=no).
|
||||
#GENPWD_DEFAULT_UPPERCASE=1
|
||||
|
||||
# genpwd: Include lowercase letters (1=yes, 0=no).
|
||||
#GENPWD_DEFAULT_LOWERCASE=1
|
||||
|
||||
# genpwd: Include digits (1=yes, 0=no).
|
||||
#GENPWD_DEFAULT_NUMBERS=1
|
||||
|
||||
# pwdscore: Show verbose breakdown by default (1=yes, 0=no).
|
||||
#PWDSCORE_DEFAULT_VERBOSE=0
|
||||
|
||||
# ==============================================================================
|
||||
[rain]
|
||||
# rain: Falling speed — integer/100 gives seconds (5 → 0.05 s).
|
||||
# Values < 1 are used as raw seconds.
|
||||
#RAIN_DEFAULT_SPEED=5
|
||||
|
||||
# rain: Colour theme. Supported: white (default), green, blue, red, yellow, cyan
|
||||
#RAIN_DEFAULT_COLOR=white
|
||||
|
||||
# matrix: Falling speed.
|
||||
#MATRIX_DEFAULT_SPEED=3.5
|
||||
|
||||
# matrix: Colour theme. Supported: green (default), blue, red, yellow, cyan, white
|
||||
#MATRIX_DEFAULT_COLOR=green
|
||||
|
||||
# matrix: Character set. Supported: binary (default), kana, ascii
|
||||
#MATRIX_DEFAULT_CHARSET=binary
|
||||
|
||||
# ==============================================================================
|
||||
[ssh]
|
||||
# ssr: Default SSH options prepended to every ssr call.
|
||||
# Examples:
|
||||
# SSH_DEFAULT_OPT=-Y # X11 forwarding
|
||||
# SSH_DEFAULT_OPT=-Y -o StrictHostKeyChecking=accept-new
|
||||
# SSH_DEFAULT_OPT= # no default options
|
||||
#SSH_DEFAULT_OPT=-Y
|
||||
|
||||
# ==============================================================================
|
||||
[updates]
|
||||
# Git branch used for update checks and profile_upgrade.
|
||||
# Changing this causes profile_upgrade to automatically switch the local
|
||||
# checkout to the new branch on the next run and display a warning.
|
||||
#UPDT_DEFAULT_BRANCH=master
|
||||
|
||||
# ==============================================================================
|
||||
[general]
|
||||
# General-purpose section — set any environment variable you need globally.
|
||||
# Good place for compilation flags, personal PATH additions, etc.
|
||||
#CFLAGS=-O2 -pipe -march=native
|
||||
#CXXFLAGS=$CFLAGS
|
||||
#MAKEFLAGS=-j4
|
||||
#PKGSOURCES=/usr/local/src
|
||||
|
||||
# ==============================================================================
|
||||
[aliases]
|
||||
# Command aliases loaded for interactive shells only.
|
||||
# The value is the full command string; the key becomes the alias name.
|
||||
# The base command must be executable; if not, the alias is silently skipped.
|
||||
#
|
||||
# ls
|
||||
ll=ls -laFh --color=auto
|
||||
la=ls -Ah --color=auto
|
||||
l=ls -CF --color=auto
|
||||
ls=ls --color=auto
|
||||
#
|
||||
# grep
|
||||
grep=grep --color=auto
|
||||
egrep=egrep --color=auto
|
||||
fgrep=fgrep --color=auto
|
||||
#
|
||||
# disk usage
|
||||
df=df -H
|
||||
du=du -ch
|
||||
#
|
||||
# make shortcuts
|
||||
#mk=make
|
||||
#mkin=make install
|
||||
|
||||
# End of profile.conf.example
|
||||
274
doc/profile.conf.fatalerrors
Executable file
274
doc/profile.conf.fatalerrors
Executable file
@@ -0,0 +1,274 @@
|
||||
[system]
|
||||
# System section is used to set Bash behavior and other system related
|
||||
# variables, such as the default pager, the terminal type, etc.
|
||||
# Set bash history
|
||||
HISTSIZE=50000
|
||||
HISTIGNORE="&:[bf]g:exit"
|
||||
|
||||
# Set default pager
|
||||
PAGER=less
|
||||
|
||||
# Set terminal colors behavior
|
||||
TERM=xterm-256color
|
||||
|
||||
[compress]
|
||||
# Section used by compress.sh (taz and utaz functions).
|
||||
|
||||
# taz: Default archive format when -f/--format is not specified.
|
||||
# Supported values: lz (default), xz, bz2, gz, lzo, tar
|
||||
#TAZ_DEFAULT_FORMAT=lz
|
||||
|
||||
# taz: Default number of compression threads when -p/--parallel is not specified.
|
||||
# Set to the number of CPU cores to use parallel compression where supported.
|
||||
#TAZ_DEFAULT_THREADS=1
|
||||
|
||||
# taz: Default compression level when none is given (1=fast/big .. 9=slow/small).
|
||||
#TAZ_DEFAULT_LEVEL=6
|
||||
|
||||
# utaz: Delete source archives after successful extraction (0=no, 1=yes).
|
||||
#UTAZ_DEFAULT_DELETE=0
|
||||
|
||||
# utaz: Default directory creation mode when neither --create-dir nor --no-dir is given.
|
||||
# Supported values:
|
||||
# auto (default) — create a subdirectory only when the archive contains
|
||||
# multiple top-level entries or a bare file
|
||||
# always — always extract into a new subdirectory
|
||||
# never — always flatten extraction into the current directory
|
||||
#UTAZ_DEFAULT_DIR_MODE=auto
|
||||
|
||||
[debug]
|
||||
# Section used by debug.sh (nothing yet)
|
||||
|
||||
[disp]
|
||||
# Section used by disp.sh
|
||||
|
||||
# Set to any value to disable colors in internal profile output (not controling binary output)
|
||||
# NO_COLOR=1
|
||||
|
||||
[filefct]
|
||||
# Section used by filefct.sh
|
||||
|
||||
# expandlist: Default output separator between items (default: space).
|
||||
# Use \n for newline, or any other character/string.
|
||||
#EXPANDLIST_DEFAULT_SEPARATOR=" "
|
||||
|
||||
# clean: Enable recursive mode by default (0=no, 1=yes).
|
||||
#CLEAN_DEFAULT_RECURSIVE=0
|
||||
|
||||
# rmspc: Default character used to replace spaces in filenames (default: underscore).
|
||||
# Set to empty to concatenate words without separator (equivalent to --subst-char with no value).
|
||||
#RMSPC_DEFAULT_CHAR=_
|
||||
|
||||
# findbig: Default number of results to return (default: 10).
|
||||
#FINDBIG_DEFAULT_LIMIT=10
|
||||
|
||||
[fun]
|
||||
# Section used by fun.sh (busy function).
|
||||
|
||||
# busy: Default hex pattern to search for in /dev/urandom hexdump (default: "ca fe").
|
||||
#BUSY_DEFAULT_PATTERN=ca fe
|
||||
|
||||
# busy: Default delay between matched lines in milliseconds (default: 0 = no delay).
|
||||
#BUSY_DEFAULT_DELAY=0
|
||||
|
||||
[info]
|
||||
# Section used by info.sh
|
||||
|
||||
# meteo: Default city used when no city argument is given.
|
||||
# Unset or empty disables the fallback and requires an explicit city argument.
|
||||
METEO_DEFAULT_CITY="Toulouse"
|
||||
|
||||
[lang]
|
||||
# Section used by lang.sh
|
||||
# List of locale shortcuts to build, in the form "shortcut:locale,...".
|
||||
# Generate a function setXX for each shortcut defined.
|
||||
SET_LOCALE="fr:fr_FR.UTF-8,us:en_US.UTF-8"
|
||||
|
||||
[net]
|
||||
# Section used by net.sh (dwl, myextip functions).
|
||||
|
||||
# dwl: Force a specific download tool instead of auto-detecting (curl → wget → fetch).
|
||||
# Supported values: curl, wget, fetch. Unset uses auto-detection (default).
|
||||
#DWL_PREFERRED_TOOL=curl
|
||||
|
||||
# myextip: API endpoint URL used to retrieve external IP information.
|
||||
# Default: https://ip-api.com/json/
|
||||
# Compatible alternatives: https://ipinfo.io/json, https://ip-api.com/json/
|
||||
#MYEXTIP_DEFAULT_URL=https://ip-api.com/json/
|
||||
|
||||
[packages]
|
||||
# Section used by packages.sh
|
||||
|
||||
# pkgs: Enable case-insensitive search by default (1=yes, 0=no; default: 0).
|
||||
#PKGS_DEFAULT_IGNORE_CASE=0
|
||||
|
||||
[processes]
|
||||
# Section used by processes.sh
|
||||
|
||||
# ppu: Output columns passed to ps -o when listing processes for a user.
|
||||
# Comma-separated list of ps field names. Default: pid,user,%cpu,%mem,start,time,command
|
||||
#PPU_DEFAULT_FORMAT=pid,user,%cpu,%mem,start,time,command
|
||||
|
||||
# ku: Signal sent to processes when killing a user's session (default: TERM).
|
||||
# Use signal names without the SIG prefix (e.g. TERM, KILL, HUP).
|
||||
#KU_DEFAULT_SIGNAL=TERM
|
||||
|
||||
[prompt]
|
||||
# Section used by prompt.sh
|
||||
|
||||
# Name of the theme to load, or an explicit path to a .theme file.
|
||||
# Bare names are resolved as $PROMPT_THEME_DIR/<name>.theme.
|
||||
# When unset, no theme is loaded and the hardcoded fallback colours are used.
|
||||
#PROMPT_THEME=default
|
||||
|
||||
# Directory that contains .theme files. Defaults to profile.d/themes/ inside
|
||||
# the profile installation directory.
|
||||
#PROMPT_THEME_DIR=/path/to/themes
|
||||
|
||||
# Individual colour overrides. These always win over the loaded theme.
|
||||
# Values must be valid ANSI escape sequences as exported by disp.sh, e.g.:
|
||||
# \e[0;34m (Blue) \e[42m (On_Green) \e[1;32m (BGreen)
|
||||
# All keys listed below correspond to variables exported by disp.sh.
|
||||
|
||||
# Clock segment
|
||||
#PROMPT_COLOR_TIME_FG=$Blue
|
||||
#PROMPT_COLOR_TIME_BG=$On_IBlack
|
||||
|
||||
# Main bar background (success and info)
|
||||
#PROMPT_COLOR_BAR_BG=$On_Blue
|
||||
|
||||
# Exit-code segment — success state
|
||||
#PROMPT_COLOR_OK_FG=$White
|
||||
#PROMPT_COLOR_OK_MARK=$Green
|
||||
|
||||
# Exit-code segment — failure state
|
||||
#PROMPT_COLOR_ERR_BG=$On_Red
|
||||
#PROMPT_COLOR_ERR_FG=$White
|
||||
#PROMPT_COLOR_ERR_MARK=$BYellow
|
||||
|
||||
# User/host colours
|
||||
#PROMPT_COLOR_ROOT_FG=$Red
|
||||
#PROMPT_COLOR_USER_FG=$Green
|
||||
|
||||
# Working directory
|
||||
#PROMPT_COLOR_DIR_FG=$ICyan
|
||||
|
||||
[pwd]
|
||||
# Section used by pwd.sh (genpwd and pwdscore functions).
|
||||
|
||||
# genpwd: Default password length (default: 16).
|
||||
#GENPWD_DEFAULT_LENGTH=16
|
||||
|
||||
# genpwd: Maximum occurrences of any single character (default: 2).
|
||||
#GENPWD_DEFAULT_OCCURS=2
|
||||
|
||||
# genpwd: Number of passwords generated when no count argument is given (default: 1).
|
||||
#GENPWD_DEFAULT_COUNT=1
|
||||
|
||||
# genpwd: Include symbols in the character pool (1=yes, 0=no; default: 1).
|
||||
#GENPWD_DEFAULT_SYMBOLS=1
|
||||
|
||||
# genpwd: Include uppercase letters in the character pool (1=yes, 0=no; default: 1).
|
||||
#GENPWD_DEFAULT_UPPERCASE=1
|
||||
|
||||
# genpwd: Include lowercase letters in the character pool (1=yes, 0=no; default: 1).
|
||||
#GENPWD_DEFAULT_LOWERCASE=1
|
||||
|
||||
# genpwd: Include digits in the character pool (1=yes, 0=no; default: 1).
|
||||
#GENPWD_DEFAULT_NUMBERS=1
|
||||
|
||||
# pwdscore: Show verbose breakdown by default (1=yes, 0=no; default: 0).
|
||||
#PWDSCORE_DEFAULT_VERBOSE=0
|
||||
|
||||
[rain]
|
||||
# Section used by rain.sh (rain and matrix functions).
|
||||
|
||||
# rain: Default speed value, using the /100 scale (5 => 0.050s, 10 => 0.100s).
|
||||
# Values < 1 are interpreted as raw seconds.
|
||||
#RAIN_DEFAULT_SPEED=5
|
||||
|
||||
# rain: Default color theme.
|
||||
# Supported values: white (default), green, blue, red, yellow, cyan
|
||||
#RAIN_DEFAULT_COLOR=white
|
||||
|
||||
# matrix: Default speed value, using the /100 scale (3.5 => 0.035s).
|
||||
#MATRIX_DEFAULT_SPEED=3.5
|
||||
|
||||
# matrix: Default color theme.
|
||||
# Supported values: green (default), blue, red, yellow, cyan, white
|
||||
#MATRIX_DEFAULT_COLOR=green
|
||||
|
||||
# matrix: Default character set.
|
||||
# Supported values: binary (default), kana, ascii
|
||||
MATRIX_DEFAULT_CHARSET=kana
|
||||
|
||||
[ssh]
|
||||
# Section used by ssh.sh
|
||||
|
||||
# ssr: Default SSH options prepended to every ssr invocation.
|
||||
# Options are word-split, so space-separated flags are supported.
|
||||
# The default behaviour without this key is equivalent to: SSH_DEFAULT_OPT=-Y
|
||||
# Set to an empty value to pass no default options.
|
||||
# Examples:
|
||||
# SSH_DEFAULT_OPT=-Y # X11 forwarding (original default)
|
||||
# SSH_DEFAULT_OPT=-Y -o StrictHostKeyChecking=accept-new
|
||||
# SSH_DEFAULT_OPT= # no default options
|
||||
#SSH_DEFAULT_OPT=-Y
|
||||
|
||||
[updates]
|
||||
# Section used by updates.sh
|
||||
#
|
||||
# UPDT_DEFAULT_BRANCH — Git branch used for update checks and upgrades.
|
||||
# Defaults to 'master' when unset. Changing this value will cause
|
||||
# profile_upgrade to automatically switch the local checkout to the new
|
||||
# branch on the next upgrade and display a warning.
|
||||
#UPDT_DEFAULT_BRANCH=master
|
||||
|
||||
[general]
|
||||
# General section allow to set any variable that can be used by the user.
|
||||
# It is also a good place to set freely global variables for personal use.
|
||||
# Set some compiling values
|
||||
CFLAGS="-O2 -pipe -march=native"
|
||||
CXXFLAGS="$CFLAGS"
|
||||
MAKEFLAGS='-j12'
|
||||
PKGSOURCES='/share/src/archives'
|
||||
|
||||
[aliases]
|
||||
# Aliases section is used to set user aliases, it is loaded only for
|
||||
# interactive shells.
|
||||
# Various ls aliases
|
||||
ll='ls -laFh --color=auto'
|
||||
la='ls -Ah --color=auto'
|
||||
l='ls -CF --color=auto'
|
||||
ls='ls --color=auto'
|
||||
|
||||
# Add color to grep output
|
||||
grep='grep --color=auto'
|
||||
egrep='egrep --color=auto'
|
||||
fgrep='fgrep --color=auto'
|
||||
|
||||
# Quick find alias
|
||||
qfind="find . -name "
|
||||
|
||||
# Some alias for compiling
|
||||
mk='make'
|
||||
mkck='make check'
|
||||
mkin='make install'
|
||||
mkdin='make DESTDIR=$PWD/dest-install install'
|
||||
|
||||
# ssh alias with X11 forwarding, without right restriction
|
||||
ssh='ssh -Y'
|
||||
|
||||
# Resume mode for wget
|
||||
wget='wget -c' # resume mode by default
|
||||
|
||||
# Human readable by default
|
||||
df='df -H'
|
||||
du='du -ch'
|
||||
sdu='du -sk ./* | sort -n'
|
||||
hdu='du -hs ./* | sort -H'
|
||||
|
||||
# Readable dmesg timestamps
|
||||
dmesg='dmesg -T'
|
||||
|
||||
# End of profile.conf
|
||||
114
doc/todo.md
Executable file
114
doc/todo.md
Executable file
@@ -0,0 +1,114 @@
|
||||
# profile — to-do list
|
||||
|
||||
Items marked **[easy]**, **[medium]**, or **[hard]** give a rough effort hint.
|
||||
Items marked **[breaking]** would change existing behaviour and require a
|
||||
version-bump.
|
||||
|
||||
---
|
||||
|
||||
## Shell / compatibility
|
||||
|
||||
- [ ] **ZSH compatibility layer** — most functions work already; the remaining
|
||||
blockers are `local -A` (no associative arrays in ZSH without `typeset -A`)
|
||||
and `local -n` namerefs. A thin compatibility shim would open the project to
|
||||
ZSH users. **[hard]**
|
||||
- [ ] **Bash completion** — add a more bash completion directory and write
|
||||
`_profile_upgrade`, `_taz`, `_utaz`, `_meteo`, etc. completions so that
|
||||
`<Tab>` works on all public functions. **[medium]**
|
||||
|
||||
---
|
||||
|
||||
## Prompt & theming
|
||||
|
||||
- [ ] **Git branch in prompt** — show the current branch name (and dirty
|
||||
indicator) in the PS1 bar when inside a Git repository. Should be
|
||||
gated behind a `[prompt]` config key so it can be disabled. **[medium]**
|
||||
- [ ] **Virtual-env / conda indicator** — detect `$VIRTUAL_ENV` / `$CONDA_DEFAULT_ENV`
|
||||
and display the name in the prompt bar. **[easy]**
|
||||
- [ ] **True-colour terminal auto-detection** — query `$COLORTERM` and
|
||||
`$TERM` at load time; automatically fall back from a 24-bit theme to its
|
||||
16-colour equivalent when the terminal does not support true colour. **[medium]**
|
||||
- [ ] **True-colour variants of other themes** — create `monokai-tc.theme`,
|
||||
`abyss-tc.theme`, etc. using the same `\e[38;2;R;G;Bm` approach as the
|
||||
Solarized themes. **[easy]** _(per theme)_
|
||||
- [ ] **Theme preview command** — add a `theme_preview` (or `profile_theme`)
|
||||
function that renders a colour swatch and a sample prompt line for the
|
||||
currently loaded theme, so users can evaluate themes without reloading
|
||||
the session. **[medium]**
|
||||
- [ ] **`load_theme` — DEFAULTCOL rebuild** — after overriding `DEFAULTFG` and
|
||||
`DEFAULTBG`, automatically recompute `DEFAULTCOL` from the new values instead
|
||||
of requiring the theme file to set it explicitly. **[easy]**
|
||||
|
||||
---
|
||||
|
||||
## Module improvements
|
||||
|
||||
### compress
|
||||
- [ ] **`taz` progress bar** — show a `pv` / `dd`-based progress indicator when
|
||||
compressing large trees, gated behind a `-p` flag. **[medium]**
|
||||
- [ ] **`utaz` integrity check** — run `tar -tOf` / `unzip -t` / `7z t` before
|
||||
extracting and abort if the archive is corrupt. **[easy]**
|
||||
|
||||
### filefct
|
||||
- [ ] **`findbig` / `findzero` / `finddead` — `fd` integration** — optionally
|
||||
use `fd` instead of `find` when available for faster traversal. **[easy]**
|
||||
- [ ] **`file_stats` — human-readable totals** — add `--human` flag to emit
|
||||
sizes in K/M/G instead of bytes. **[easy]**
|
||||
|
||||
### info
|
||||
- [ ] **`showinfo` fallback** — when neither `neofetch` nor `fastfetch` is
|
||||
installed, print a minimal sysinfo block (hostname, OS, kernel, uptime,
|
||||
CPU, RAM) using pure Bash + `/proc`. **[medium]**
|
||||
|
||||
### net
|
||||
- [ ] **`dwl` resume support** — pass `-C -` to curl / `--continue-at -` to
|
||||
wget for interrupted downloads; gate behind a `-r` flag. **[easy]**
|
||||
- [ ] **`myextip` multiple providers** — fall back to a secondary URL
|
||||
(configurable via `MYEXTIP_FALLBACK_URL`) when the primary times out.
|
||||
**[easy]**
|
||||
|
||||
### processes
|
||||
- [ ] **`ku` dry-run flag** — add `-n` / `--dry-run` to print what would be
|
||||
killed without acting. **[easy]**
|
||||
|
||||
### pwd
|
||||
- [ ] **`genpwd` passphrase mode** — add `-w` / `--words N` to generate
|
||||
word-based passphrases (diceware-style) from `/usr/share/dict/words`.
|
||||
**[medium]**
|
||||
|
||||
### ssh
|
||||
- [ ] **SSH agent management** — add `ssh_agent_start` / `ssh_agent_stop` helpers
|
||||
that start a persistent `ssh-agent`, add configured keys, and survive
|
||||
re-login via a socket stored in `~/.ssh/agent.env`. **[medium]**
|
||||
- [ ] **`rmhost` glob support** — allow `rmhost '*.example.com'` to remove all
|
||||
matching entries in one call. **[easy]**
|
||||
|
||||
### updates
|
||||
- [ ] **Automatic update check age** — store a timestamp in `~/.cache/profile_last_check`;
|
||||
skip the network request in `check_updates -q` if the last check was less
|
||||
than `UPDT_CHECK_INTERVAL` hours ago (configurable, default 24). **[medium]**
|
||||
- [ ] **Changelog display** — after a successful `profile_upgrade`, fetch and
|
||||
display `CHANGELOG.md` entries newer than the previously installed version.
|
||||
**[medium]**
|
||||
|
||||
---
|
||||
|
||||
## Infrastructure
|
||||
|
||||
- [ ] **Test suite** — add a `test/` directory with `bats` (Bash Automated
|
||||
Testing System) unit tests for pure functions (`expandlist`, `genpwd`,
|
||||
`pwdscore`, `isipv4`, `isipv6`, `parse_conf`, `load_theme`, …). Target for
|
||||
CI. **[hard]**
|
||||
- [ ] **CI pipeline** — Gitea Actions (or similar) job that runs `shellcheck`
|
||||
and the `bats` suite on every push. **[medium]**
|
||||
- [ ] **`profile_status` function** — print a diagnostic summary: installed
|
||||
version, active theme, loaded modules, detected Bash version, interactive /
|
||||
login shell flags, and `profile.conf` path. **[easy]**
|
||||
- [ ] **`profile_uninstall` function** — remove the `source` line from
|
||||
`~/.bashrc` / `~/.profile` and optionally delete the install directory,
|
||||
with a dry-run mode. **[medium]**
|
||||
- [ ] **`disp` syslog integration** — add a `DISP_SYSLOG=1` config key that
|
||||
additionally pipes E/W messages to `logger`. **[easy]**
|
||||
- [ ] **XDG base-dir support** — honour `$XDG_CONFIG_HOME` as an alternative
|
||||
location for `profile.conf` so users can keep `~` tidy. **[medium]**
|
||||
**[breaking]**
|
||||
144
history.txt
144
history.txt
@@ -1,144 +0,0 @@
|
||||
------------------------------------------------------------------------------
|
||||
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.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
|
||||
|
||||
220
profile.d/bash-completion/git-completion.sh
Normal file
220
profile.d/bash-completion/git-completion.sh
Normal file
@@ -0,0 +1,220 @@
|
||||
#!/usr/bin/env bash
|
||||
# ------------------------------------------------------------------------------
|
||||
# Git helper completions for profile.d/git.sh shortcuts.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Return 0 when current directory is inside a git work tree.
|
||||
_profile_git_in_repo()
|
||||
{
|
||||
git rev-parse --is-inside-work-tree >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# Load git completion helpers on demand if they are available on the system.
|
||||
_profile_git_load_completion_helpers()
|
||||
{
|
||||
declare -F __git_complete >/dev/null 2>&1 && return 0
|
||||
|
||||
local completion_file
|
||||
for completion_file in \
|
||||
/usr/share/bash-completion/completions/git \
|
||||
/usr/share/git/completion/git-completion.bash \
|
||||
/etc/bash_completion.d/git
|
||||
do
|
||||
if [[ -r "$completion_file" ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
. "$completion_file"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
declare -F __git_complete >/dev/null 2>&1
|
||||
}
|
||||
|
||||
_profile_git_complete_remotes()
|
||||
{
|
||||
local cur
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
|
||||
if ! _profile_git_in_repo; then
|
||||
COMPREPLY=()
|
||||
return 0
|
||||
fi
|
||||
|
||||
COMPREPLY=( $(compgen -W "$(git remote 2>/dev/null)" -- "$cur") )
|
||||
}
|
||||
|
||||
_profile_git_complete_refs()
|
||||
{
|
||||
local cur
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
|
||||
if ! _profile_git_in_repo; then
|
||||
COMPREPLY=()
|
||||
return 0
|
||||
fi
|
||||
|
||||
COMPREPLY=( $(compgen -W "$(git for-each-ref --format='%(refname:short)' refs/heads refs/remotes refs/tags 2>/dev/null)" -- "$cur") )
|
||||
}
|
||||
|
||||
_profile_git_complete_add_paths()
|
||||
{
|
||||
# shellcheck disable=SC2034 # Used indirectly by git-completion helpers via dynamic scope.
|
||||
local cur words cword prev __git_cmd_idx=0
|
||||
local complete_opt="--others --modified --directory --no-empty-directory"
|
||||
|
||||
if declare -F __git_complete_index_file >/dev/null 2>&1; then
|
||||
if declare -F _get_comp_words_by_ref >/dev/null 2>&1; then
|
||||
_get_comp_words_by_ref -n =: cur words cword prev
|
||||
else
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
if (( COMP_CWORD > 0 )); then
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
else
|
||||
prev=""
|
||||
fi
|
||||
# shellcheck disable=SC2034 # Used indirectly by git-completion helpers via dynamic scope.
|
||||
cword="$COMP_CWORD"
|
||||
# shellcheck disable=SC2034 # Used indirectly by git-completion helpers via dynamic scope.
|
||||
words=("${COMP_WORDS[@]}")
|
||||
fi
|
||||
|
||||
if [[ -n $(__git_find_on_cmdline "-u --update") ]]; then
|
||||
complete_opt="--modified"
|
||||
fi
|
||||
__git_complete_index_file "$complete_opt"
|
||||
return 0
|
||||
fi
|
||||
|
||||
COMPREPLY=( $(compgen -f -- "${COMP_WORDS[COMP_CWORD]}") )
|
||||
}
|
||||
|
||||
_complete_gst()
|
||||
{
|
||||
local cur
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $(compgen -W "-h --help" -- "$cur") )
|
||||
;;
|
||||
*)
|
||||
COMPREPLY=( $(compgen -d -- "$cur") )
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_complete_ggraph()
|
||||
{
|
||||
local cur prev
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
|
||||
case "$prev" in
|
||||
-n|--limit)
|
||||
COMPREPLY=()
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
COMPREPLY=( $(compgen -W "-h --help -n --limit" -- "$cur") )
|
||||
}
|
||||
|
||||
_complete_gsync()
|
||||
{
|
||||
local cur
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
|
||||
if [[ $cur == -* ]]; then
|
||||
COMPREPLY=( $(compgen -W "-h --help" -- "$cur") )
|
||||
return 0
|
||||
fi
|
||||
|
||||
_profile_git_complete_remotes
|
||||
}
|
||||
|
||||
_complete_gacp()
|
||||
{
|
||||
local cur prev
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
|
||||
case "$prev" in
|
||||
-m|--message)
|
||||
COMPREPLY=()
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ $cur == -* ]]; then
|
||||
COMPREPLY=( $(compgen -W "-h --help -a --auto -m --message" -- "$cur") )
|
||||
return 0
|
||||
fi
|
||||
|
||||
_profile_git_complete_add_paths
|
||||
}
|
||||
|
||||
_complete_greset()
|
||||
{
|
||||
local cur
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
|
||||
if [[ $cur == -* ]]; then
|
||||
COMPREPLY=( $(compgen -W "-h --help -x --with-ignored" -- "$cur") )
|
||||
return 0
|
||||
fi
|
||||
|
||||
_profile_git_complete_refs
|
||||
}
|
||||
|
||||
_complete_gwip()
|
||||
{
|
||||
local cur
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
|
||||
if [[ $cur == -* ]]; then
|
||||
COMPREPLY=( $(compgen -W "-h --help" -- "$cur") )
|
||||
else
|
||||
COMPREPLY=()
|
||||
fi
|
||||
}
|
||||
|
||||
_complete_gprune()
|
||||
{
|
||||
local cur
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
|
||||
if [[ $cur == -* ]]; then
|
||||
COMPREPLY=( $(compgen -W "-h --help" -- "$cur") )
|
||||
return 0
|
||||
fi
|
||||
|
||||
_profile_git_complete_refs
|
||||
}
|
||||
|
||||
_complete_groot()
|
||||
{
|
||||
local cur
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
|
||||
COMPREPLY=( $(compgen -W "-h --help -g --go" -- "$cur") )
|
||||
}
|
||||
|
||||
_profile_git_register_completions()
|
||||
{
|
||||
complete -F _complete_gst gst
|
||||
complete -F _complete_ggraph ggraph
|
||||
complete -F _complete_gsync gsync
|
||||
complete -F _complete_gacp gacp
|
||||
complete -F _complete_greset greset
|
||||
complete -F _complete_gwip gwip
|
||||
complete -F _complete_gprune gprune
|
||||
complete -F _complete_groot groot
|
||||
}
|
||||
|
||||
# Register completions only in interactive bash sessions.
|
||||
if [[ $- == *i* && -n ${BASH_VERSION:-} ]]; then
|
||||
_profile_git_load_completion_helpers >/dev/null 2>&1 || true
|
||||
_profile_git_register_completions
|
||||
fi
|
||||
|
||||
# EOF
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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,
|
||||
@@ -35,40 +35,51 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Smartly uncompress archives (zip only for now)
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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()
|
||||
{
|
||||
# shellcheck disable=SC2329
|
||||
_ununzip()
|
||||
{
|
||||
unzip -o "$1" -d "$2" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_untar()
|
||||
{
|
||||
tar -xf "$1" -C "$2"
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_ungzip()
|
||||
{
|
||||
tar -xzf "$1" -C "$2"
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_unbzip2()
|
||||
{
|
||||
tar -xjf "$1" -C "$2"
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_unxz()
|
||||
{
|
||||
tar -xJf "$1" -C "$2"
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_unlzop()
|
||||
{
|
||||
lzop -d "$1" -o "$2/$(basename "${1%.*}")"
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_unlzip()
|
||||
{
|
||||
if command -v plzip >/dev/null 2>&1; then
|
||||
@@ -78,57 +89,67 @@ utaz()
|
||||
fi
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_ununrar()
|
||||
{
|
||||
unrar x -o+ "$1" "$2/" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_ununarj()
|
||||
{
|
||||
unarj e "$1" "$2/" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
_unlza()
|
||||
# shellcheck disable=SC2329
|
||||
_unlha()
|
||||
{
|
||||
# lha typically extracts into the current directory
|
||||
# We ensure it hits the target directory
|
||||
(cd "$2" && lha -x "../$1") >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_ununace()
|
||||
{
|
||||
unace x "$1" "$2/" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_un7z()
|
||||
{
|
||||
7z x "$1" -o"$2/" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_unzstd()
|
||||
{
|
||||
# Zstd decompresses files directly, often requiring tar for archives
|
||||
tar --zstd -xf "$1" -C "$2"
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_uncpio()
|
||||
{
|
||||
# CPIO requires careful directory handling
|
||||
(cd "$2" && cpio -id < "../$1") >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_uncabextract()
|
||||
{
|
||||
# Requires 'cabextract' package
|
||||
cabextract "$1" -d "$2/" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_undeb()
|
||||
{
|
||||
# Extracts data content from a Debian package
|
||||
dpkg-deb -x "$1" "$2/" >/dev/null 2>&1
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_unrpm()
|
||||
{
|
||||
# Extracts CPIO-based payload from an RPM package
|
||||
@@ -136,54 +157,99 @@ utaz()
|
||||
rpm2cpio "$1" | (cd "$2/" && cpio -idmv) >/dev/null 2>&1
|
||||
}
|
||||
|
||||
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
|
||||
local PARSED
|
||||
PARSED=$(getopt -o hdcn --long help,delete,create-dir,no-dir -n 'utaz' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [ $? -ne 0 ]; then
|
||||
disp E "Invalid options, use \"utaz --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
eval set -- "$PARSED"
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "utaz: uncompress all the given files and/or the ones found in the given\n"
|
||||
printf " directories creating an host directory where needed.\n\n"
|
||||
printf "Usage: utaz [option] [directorie(s)|file(s)]\n\n"
|
||||
printf "Options:\n"
|
||||
printf "\t-h, --help\t\tDisplay that help screen\n"
|
||||
printf "\t-d, --delete\t\tIf decompression succeeded, delete the source file\n"
|
||||
printf "\t-c, --create-dir\tAlways create a host directory\n"
|
||||
printf "\t-n, --no-dir\t\tNever create a host directory\n\n"
|
||||
printf "Supported archive format:\n"
|
||||
printf "\t- zip\n"
|
||||
printf "\t- tar.gz, .tgz\n"
|
||||
printf "\t- tar.bz2, .tbz2\n"
|
||||
printf "\t- tar.xz, .txz\n"
|
||||
printf "\t- tar.lz, .tlz\n"
|
||||
printf "\t- rar\n"
|
||||
printf "\t- arj\n"
|
||||
printf "\t- lha, lzh\n"
|
||||
printf "\t- ace\n"
|
||||
printf "\t- 7z, p7z\n"
|
||||
printf "\t- zst\n"
|
||||
printf "\t- cpio\n"
|
||||
printf "\t- cab\n"
|
||||
printf "\t- deb\n"
|
||||
printf "\t- rpm\n"
|
||||
return 0
|
||||
;;
|
||||
|
||||
"-d" | "--delete")
|
||||
-d|--delete)
|
||||
local willrm=1
|
||||
shift
|
||||
;;
|
||||
|
||||
"-c" | "--create-dir")
|
||||
-c|--create-dir)
|
||||
local createdir=1
|
||||
shift
|
||||
;;
|
||||
|
||||
"-n" | "--no-dir")
|
||||
-n|--no-dir)
|
||||
local nodir=1
|
||||
shift
|
||||
;;
|
||||
|
||||
"-"*)
|
||||
disp E "Invalid option, use \"utaz --help\" to display options list"
|
||||
echo
|
||||
return 1
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
|
||||
*)
|
||||
# The ${opt%/} writing is to remove trailing / if any
|
||||
local LIST="${LIST} ${opt%/}"
|
||||
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=(".")
|
||||
|
||||
# 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
|
||||
|
||||
[[ -n ${createdir} && -n ${nodir} ]] && \
|
||||
disp E "The --create-dir and --no-dir options are mutually exclusive."
|
||||
|
||||
[[ -z ${LIST} ]] && local LIST="."
|
||||
for zitem in ${LIST}; do
|
||||
for f in "${zitem}"/*; do
|
||||
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 "${targets[@]}"; do
|
||||
local dir="${f%.*}"
|
||||
local extractor=""
|
||||
case "$f" in
|
||||
@@ -193,13 +259,13 @@ utaz()
|
||||
*.tar.gz|*.tgz)
|
||||
extractor="_ungzip"
|
||||
;;
|
||||
*.tar.bz2)
|
||||
*.tar.bz2|*.tbz2)
|
||||
extractor="_unbzip2"
|
||||
;;
|
||||
*.tar.xz)
|
||||
*.tar.xz|*.txz)
|
||||
extractor="_unxz"
|
||||
;;
|
||||
*.tar.lz)
|
||||
*.tar.lz|*.tlz)
|
||||
extractor="_unlzop"
|
||||
;;
|
||||
*.tar)
|
||||
@@ -217,7 +283,7 @@ utaz()
|
||||
*.ace)
|
||||
extractor="_ununace"
|
||||
;;
|
||||
*.7z)
|
||||
*.7z|*.p7z)
|
||||
extractor="_un7z"
|
||||
;;
|
||||
*.zst)
|
||||
@@ -244,30 +310,31 @@ utaz()
|
||||
# Verify binary existence
|
||||
local cmd=${extractor//_un/}
|
||||
if [[ $cmd == "deb" ]]; then
|
||||
command -v dpkg-deb >/dev/null 2>&1 || {
|
||||
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 || {
|
||||
command -v -- rpm2cpio >/dev/null 2>&1 || {
|
||||
disp E "The program 'rpm2cpio' is not installed, aborting."
|
||||
continue
|
||||
}
|
||||
command -v cpio >/dev/null 2>&1 || {
|
||||
command -v -- cpio >/dev/null 2>&1 || {
|
||||
disp E "The program 'cpio' is not installed, aborting."
|
||||
continue
|
||||
}
|
||||
else
|
||||
command -v ${cmd} >/dev/null 2>&1 || {
|
||||
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 ]] &&
|
||||
if ! mkdir -p "${dir}"; then
|
||||
disp E "The filesystem can't create directories, exit!" &&
|
||||
return 1
|
||||
fi
|
||||
|
||||
${extractor} "${f}" "${dir}"
|
||||
case $? in
|
||||
@@ -276,10 +343,14 @@ utaz()
|
||||
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 zip file seems corrupted, failed."
|
||||
disp E "The compressed file ${f} seems corrupted, failed."
|
||||
rm -rf "${dir}" >/dev/null 2>&1
|
||||
continue
|
||||
;;
|
||||
@@ -288,7 +359,12 @@ utaz()
|
||||
if [[ -n ${createdir} ]]; then
|
||||
disp I "Archive extracted successfully in subdirectory."
|
||||
elif [[ -n ${nodir} ]]; then
|
||||
mv "./${dir}/"* ./ && rmdir "${dir}"
|
||||
shopt -s nullglob
|
||||
for child in "${dir}"/*; do
|
||||
mv -- "$child" .
|
||||
done
|
||||
shopt -u nullglob
|
||||
rmdir -- "${dir}"
|
||||
disp I "Archive extracted successfully, no subdirectory needed."
|
||||
else
|
||||
# Set nullglob to ensure the array is empty if no files match
|
||||
@@ -298,7 +374,12 @@ utaz()
|
||||
# Check if exactly one item exists and if that item is a directory
|
||||
if [[ ${#contents[@]} -eq 1 ]] && [[ -d "${contents[0]}" ]]; then
|
||||
# Single directory detected
|
||||
mv "${contents[0]}"/* ./ && rmdir "${dir}"
|
||||
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."
|
||||
@@ -309,12 +390,38 @@ utaz()
|
||||
done
|
||||
}
|
||||
export -f utaz
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Compress directories or files into one or more archive
|
||||
# ------------------------------------------------------------------------------
|
||||
# Usage: taz [option] [--parallel=<n|auto>] [--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, or 'auto' to use detected CPU count
|
||||
# -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()
|
||||
{
|
||||
# Resolve runtime CPU count for --parallel=auto.
|
||||
_taz_detect_cpus()
|
||||
{
|
||||
local cpus=1
|
||||
if command -v nproc >/dev/null 2>&1; then
|
||||
cpus=$(nproc 2>/dev/null)
|
||||
elif command -v getconf >/dev/null 2>&1; then
|
||||
cpus=$(getconf _NPROCESSORS_ONLN 2>/dev/null)
|
||||
fi
|
||||
|
||||
[[ $cpus =~ ^[1-9][0-9]*$ ]] || cpus=1
|
||||
printf "%s\n" "$cpus"
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_doxz()
|
||||
{
|
||||
command -v xz >/dev/null 2>&1 || {
|
||||
@@ -322,17 +429,19 @@ taz()
|
||||
return 127
|
||||
}
|
||||
|
||||
[[ $4 ]] && local verb='-v'
|
||||
local verb=()
|
||||
[[ $4 ]] && verb=('-v')
|
||||
|
||||
# Display a warning for this format
|
||||
disp W "xz format is not suited for long term archiving."
|
||||
disp I "See https://www.nongnu.org/lzip/xz_inadequate.html for details."
|
||||
|
||||
# Compresse to xz (lzma2) - Deprecated
|
||||
xz $verb --compress --keep -$3 -T $2 $1
|
||||
# Compress with xz (lzma2) - Deprecated
|
||||
xz "${verb[@]}" --compress --keep "-$3" -T "$2" "$1"
|
||||
return $?
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_dolz()
|
||||
{
|
||||
local procopt="--threads $2"
|
||||
@@ -350,13 +459,16 @@ taz()
|
||||
disp W "Consider installing plzip to obtain multithreading abilities."
|
||||
}
|
||||
|
||||
[[ $4 ]] && local verb="-vv"
|
||||
local opt=()
|
||||
[[ $4 ]] && opt=('-vv')
|
||||
opt+=("$procopt")
|
||||
|
||||
# Compresse au format lzip (lzma)
|
||||
$command $verb $procopt --keep -$3 $1
|
||||
# Compress with lzip (lzma)
|
||||
$command "${opt[@]}" --keep "-$3" "$1"
|
||||
return $?
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_dogz()
|
||||
{
|
||||
local procopt="--processes $2"
|
||||
@@ -374,13 +486,16 @@ taz()
|
||||
disp W "Consider installing pigz to obtain multithreading abilities."
|
||||
}
|
||||
|
||||
[[ $4 ]] && local verb="--verbose"
|
||||
local opt=()
|
||||
[[ $4 ]] && opt=('--verbose')
|
||||
opt+=("$procopt")
|
||||
|
||||
# Compresse au format bz2
|
||||
$command $verb $procopt --keep -$3 $1
|
||||
# Compress with gzip
|
||||
$command "${opt[@]}" --keep "-$3" "$1"
|
||||
return $?
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_dobz2()
|
||||
{
|
||||
local procopt="-p$2"
|
||||
@@ -398,13 +513,16 @@ taz()
|
||||
disp W "Consider installing pbzip2 to obtain multithreading abilities."
|
||||
}
|
||||
|
||||
[[ $4 ]] && local verb="-v"
|
||||
local opt=()
|
||||
[[ $4 ]] && opt=('-v')
|
||||
opt+=("$procopt")
|
||||
|
||||
# Compresse au format bz2
|
||||
$command $verb --compress $procopt --keep -$3 $1
|
||||
# Compress with bz2
|
||||
$command "${opt[@]}" --compress --keep "-$3" "$1"
|
||||
return $?
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
_dolzo()
|
||||
{
|
||||
command -v lzop >/dev/null 2>&1 || {
|
||||
@@ -412,94 +530,118 @@ taz()
|
||||
return 127
|
||||
}
|
||||
|
||||
[[ $4 ]] && local verb='-v'
|
||||
local verb=()
|
||||
[[ $4 ]] && verb=('-v')
|
||||
[[ $2 -gt 1 ]] && disp W "lzop doesn't support multithreading, falling back to 1 thread."
|
||||
|
||||
# Compresse au format lzo
|
||||
lzop --keep -$3 $1
|
||||
# Compress with lzo
|
||||
lzop "${verb[@]}" --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 " -q, --quiet Display less messages (only errors and warnings)"
|
||||
echo " -1, .., -9 Compression level to use [1=fast/biggest, 9=slow/smallest]"
|
||||
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
|
||||
local PARSED
|
||||
PARSED=$(getopt -o hdf:p:vq123456789 --long help,delete,format:,parallel:,verbose,quiet --name "taz" -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [ $? -ne 0 ]; then
|
||||
disp E "Invalid options, use \"taz --help\" to display usage."
|
||||
return 1
|
||||
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|auto>] [--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, or 'auto' for runtime CPU count\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")
|
||||
-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")
|
||||
-v|--verbose)
|
||||
local verbose=1
|
||||
shift
|
||||
;;
|
||||
|
||||
"-q" | "--quiet")
|
||||
-q|--quiet)
|
||||
local quiet=1
|
||||
shift
|
||||
;;
|
||||
|
||||
"-"*)
|
||||
local complevel=$(echo $opt | sed 's/-//')
|
||||
if ! [[ $complevel =~ ^[1-9]+$ ]]; then
|
||||
disp E "Invalid option, use taz --help to display options list"
|
||||
echo
|
||||
return 1
|
||||
fi
|
||||
-[1-9])
|
||||
complevel="${1#-}"
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
|
||||
*)
|
||||
local LIST="$LIST ${opt%/}"
|
||||
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=(".")
|
||||
|
||||
[[ ! $compform ]] && compform=${TAZ_DEFAULT_FORMAT:-lz}
|
||||
[[ ! $nproc ]] && nproc=${TAZ_DEFAULT_THREADS:-auto}
|
||||
[[ ! $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 $LIST; do
|
||||
# Backward compatibility: 0 previously meant auto-detect.
|
||||
[[ $nproc == 0 ]] && nproc=auto
|
||||
if [[ $nproc == auto ]]; then
|
||||
nproc=$(_taz_detect_cpus)
|
||||
elif [[ ! $nproc =~ ^[1-9][0-9]*$ ]]; then
|
||||
disp E "Invalid value for --parallel: '$nproc' (expected auto or a positive integer)."
|
||||
return 1
|
||||
fi
|
||||
|
||||
for item in "${FILES[@]}"; do
|
||||
local donetar=0
|
||||
disp I "Processing $item..."
|
||||
|
||||
if [[ -d "$item" ]]; then
|
||||
disp I "\t Creating $item.tar... "
|
||||
|
||||
tar -cf "$item.tar" "$item"
|
||||
if [[ ! $? -eq 0 ]]; then
|
||||
if ! tar -cf "$item.tar" "$item"; then
|
||||
disp E "tar file creation failed, skipping to next item."
|
||||
continue
|
||||
fi
|
||||
@@ -513,8 +655,9 @@ taz()
|
||||
# Skip compression part if tar is asked
|
||||
if [[ $compform != "tar" ]]; then
|
||||
disp I "\t Compressing archive..."
|
||||
_do$compform "$fname" "$nproc" "$complevel" "$verbose"
|
||||
[[ ! $? -eq 0 ]] && case $? in
|
||||
local exec_code=0
|
||||
"_do$compform" "$fname" "$nproc" "$complevel" "$verbose" || exec_code=$?
|
||||
[[ ! $exec_code -eq 0 ]] && case $exec_code in
|
||||
127)
|
||||
disp E "Compression program unavailable, aborting."
|
||||
return 127
|
||||
@@ -536,6 +679,9 @@ taz()
|
||||
unset quiet
|
||||
}
|
||||
export -f taz
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
load_conf "compress"
|
||||
|
||||
# EOF
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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,
|
||||
@@ -36,27 +36,22 @@
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Display a 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 "========= Call stack =========\n"
|
||||
local i=1 # We begin at 1 to ignore backtrace itself
|
||||
while [[ $i -lt ${#FUNCNAME[@]} ]]; do
|
||||
printf '%15s() %s:%d\n' \
|
||||
"$func" "${BASH_SOURCE[$i]}" "${BASH_LINENO[(($i - 1))]}"
|
||||
fi
|
||||
let i++ || true
|
||||
"${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()
|
||||
{
|
||||
local errcode=$?
|
||||
@@ -66,45 +61,71 @@ function error()
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Activate or deactivate error trapping to display backtrace
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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
|
||||
|
||||
local PARSED
|
||||
PARSED=$(getopt -oh --long help,on,off,status,force -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"settrace --help\" to display usage."
|
||||
return 1
|
||||
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" ]]; then
|
||||
disp W "ERR signal trap is already set, replacing previous trap!"
|
||||
--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
|
||||
;;
|
||||
"--off")
|
||||
if [[ $status != "on" ]]; then
|
||||
--force)
|
||||
force=1
|
||||
shift
|
||||
;;
|
||||
--off)
|
||||
if [[ ${status} != "on" ]]; then
|
||||
disp W "ERR signal trap is already unset!"
|
||||
fi
|
||||
trap - ERR
|
||||
shift
|
||||
;;
|
||||
"--status")
|
||||
disp "ERR trap signal is ${status}."
|
||||
--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
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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,
|
||||
@@ -36,117 +36,208 @@
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Color definitions
|
||||
set_colors()
|
||||
{
|
||||
# Standard 16 colors display declaration
|
||||
export DEFAULTFG='\e[0;39m'
|
||||
export DEFAULTBG='\e[0;49m'
|
||||
export DEFAULTCOL="${DEFAULTBG}${DEFAULTFG}"
|
||||
export RESETCOL=$'\e[0m'
|
||||
|
||||
# Regular Colors
|
||||
export Black='\e[0;30m'
|
||||
export Red='\e[0;31m'
|
||||
export Green='\e[0;32m'
|
||||
export Yellow='\e[0;33m'
|
||||
export Blue='\e[0;34m'
|
||||
export Purple='\e[0;35m'
|
||||
export Cyan='\e[0;36m'
|
||||
export White='\e[0;37m'
|
||||
|
||||
# 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'
|
||||
}
|
||||
export -f set_colors
|
||||
# ------------------------------------------------------------------------------
|
||||
# Standard 16 colors display declaration
|
||||
export DEFAULTFG="\e[0;39m"
|
||||
export DEFAULTBG="\e[0;49m"
|
||||
export DEFAULTCOL=${DEFAULTBG}${DEFAULTFG}
|
||||
export RESETCOL=$'\e[0m'
|
||||
|
||||
# Regular Colors
|
||||
export Black='\e[0;30m'
|
||||
export Red='\e[0;31m'
|
||||
export Green='\e[0;32m'
|
||||
export Yellow='\e[0;33m'
|
||||
export Blue='\e[0;34m'
|
||||
export Purple='\e[0;35m'
|
||||
export Cyan='\e[0;36m'
|
||||
export White='\e[0;37m'
|
||||
|
||||
# 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()
|
||||
{
|
||||
case $1 in
|
||||
_disp_print_wrapped()
|
||||
{
|
||||
local prefix="$1"
|
||||
local prefix_len="$2"
|
||||
local target_fd="$3"
|
||||
shift 3
|
||||
local message="$*"
|
||||
|
||||
local cols="${COLUMNS:-}"
|
||||
if [[ -z "$cols" || ! "$cols" =~ ^[0-9]+$ || "$cols" -lt 20 ]]; then
|
||||
cols=$(tput cols 2>/dev/null)
|
||||
fi
|
||||
[[ -z "$cols" || ! "$cols" =~ ^[0-9]+$ || "$cols" -lt 20 ]] && cols=80
|
||||
|
||||
local indent_len=0
|
||||
[[ "$prefix_len" =~ ^[0-9]+$ && "$prefix_len" -gt 0 ]] && indent_len=$((prefix_len + 1))
|
||||
|
||||
local width=$((cols - indent_len))
|
||||
(( width < 10 )) && width=10
|
||||
|
||||
local wrapped
|
||||
wrapped=$(printf "%s" "$message" | fold -s -w "$width")
|
||||
|
||||
local first_line=1
|
||||
local line
|
||||
while IFS= read -r line || [[ -n "$line" ]]; do
|
||||
if (( first_line )); then
|
||||
if [[ -n "$prefix" ]]; then
|
||||
if [[ "$target_fd" -eq 2 ]]; then
|
||||
printf "%b\n" "${prefix} ${line}${RESETCOL}" >&2
|
||||
else
|
||||
printf "%b\n" "${prefix} ${line}${RESETCOL}"
|
||||
fi
|
||||
else
|
||||
if [[ "$target_fd" -eq 2 ]]; then
|
||||
printf "%b\n" "${line}${RESETCOL}" >&2
|
||||
else
|
||||
printf "%b\n" "${line}${RESETCOL}"
|
||||
fi
|
||||
fi
|
||||
first_line=0
|
||||
else
|
||||
if [[ "$target_fd" -eq 2 ]]; then
|
||||
printf "%*s%b\n" "$indent_len" "" "${line}${RESETCOL}" >&2
|
||||
else
|
||||
printf "%*s%b\n" "$indent_len" "" "${line}${RESETCOL}"
|
||||
fi
|
||||
fi
|
||||
done <<< "$wrapped"
|
||||
}
|
||||
|
||||
# Handle NO_COLOR: disable colors if set
|
||||
local color_enabled=1
|
||||
[[ -n $NO_COLOR ]] && color_enabled=0
|
||||
|
||||
case ${1^^} in
|
||||
"I")
|
||||
local heads_plain="[ info ]"
|
||||
if [[ $color_enabled -eq 1 ]]; then
|
||||
local heads="[ ${IGreen}info${DEFAULTFG} ]"
|
||||
else
|
||||
local heads="$heads_plain"
|
||||
fi
|
||||
shift
|
||||
[[ -z $QUIET || $QUIET -ne 1 ]] && \
|
||||
printf "%b\n" "${heads} $*${RESETCOL}"
|
||||
_disp_print_wrapped "$heads" "${#heads_plain}" 1 "$*"
|
||||
;;
|
||||
"W")
|
||||
local heads_plain="[ Warning ]"
|
||||
if [[ $color_enabled -eq 1 ]]; then
|
||||
local heads="[ ${IYellow}Warning${DEFAULTFG} ]"
|
||||
else
|
||||
local heads="$heads_plain"
|
||||
fi
|
||||
shift
|
||||
printf "%b\n" "${heads} $*${RESETCOL}" >&2
|
||||
_disp_print_wrapped "$heads" "${#heads_plain}" 2 "$*"
|
||||
;;
|
||||
"E")
|
||||
local heads_plain="[ ERROR ]"
|
||||
if [[ $color_enabled -eq 1 ]]; then
|
||||
local heads="[ ${IRed}ERROR${DEFAULTFG} ]"
|
||||
else
|
||||
local heads="$heads_plain"
|
||||
fi
|
||||
shift
|
||||
printf "%b\n" "${heads} $*${RESETCOL}" >&2
|
||||
_disp_print_wrapped "$heads" "${#heads_plain}" 2 "$*"
|
||||
;;
|
||||
"D")
|
||||
local heads_plain="[ debug ]"
|
||||
if [[ $color_enabled -eq 1 ]]; then
|
||||
local heads="[ ${ICyan}debug${DEFAULTFG} ]"
|
||||
else
|
||||
local heads="$heads_plain"
|
||||
fi
|
||||
shift
|
||||
[[ -n $DEBUG && $DEBUG -gt 1 ]] && \
|
||||
printf "%b\n" "${heads} $*${RESETCOL}"
|
||||
_disp_print_wrapped "$heads" "${#heads_plain}" 1 "$*"
|
||||
;;
|
||||
* )
|
||||
[[ -z $QUIET || $QUIET -ne 1 ]] && \
|
||||
printf "%b\n" "$*"
|
||||
_disp_print_wrapped "" 0 1 "$*"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
export -f disp
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# Load disp section variables
|
||||
load_conf disp
|
||||
set_colors
|
||||
|
||||
# EOF
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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,
|
||||
@@ -35,223 +35,431 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# expandlist : treat wildcards in a file/directory list
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"expandlist --help\" to display usage."
|
||||
return 1
|
||||
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
|
||||
;;
|
||||
|
||||
"-"*)
|
||||
disp E "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
|
||||
# True glob expansion when wildcards are present.
|
||||
if [[ "$item" == *'*'* || "$item" == *'?'* || "$item" == *'['* ]]; then
|
||||
# shellcheck disable=SC2206 # We actually want the word splitting
|
||||
expanded=( $item )
|
||||
else
|
||||
echo "rm $rmopt $f"
|
||||
expanded=( "$item" )
|
||||
fi
|
||||
|
||||
if [[ ${#expanded[@]} -eq 0 ]]; then
|
||||
continue
|
||||
fi
|
||||
|
||||
for content in "${expanded[@]}"; do
|
||||
if (( matched )); then
|
||||
result+="$separator"
|
||||
fi
|
||||
result+="\"$content\""
|
||||
matched=1
|
||||
done
|
||||
done
|
||||
|
||||
shopt -u nullglob
|
||||
printf '%s\n' "$result"
|
||||
}
|
||||
export -f expandlist
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Clean a directory tree from temporary or backup files
|
||||
# Usage: clean [options] [directory1] [...[directoryX]]
|
||||
# Options:
|
||||
# -h, --help: display help screen
|
||||
# -r, --recurs: do a recursive cleaning
|
||||
# -f, --force: do not ask for confirmation (use with care)
|
||||
# -s, --shell: do nothing and display what will be executed
|
||||
clean()
|
||||
{
|
||||
local recursive=${CLEAN_DEFAULT_RECURSIVE:-0} force=0 outshell=0
|
||||
|
||||
# Define short and long options
|
||||
local PARSED
|
||||
PARSED=$(getopt -o hrsf --long help,recurs,shell,force -n 'clean' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"clean --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
|
||||
eval set -- "$PARSED"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-r|--recurs)
|
||||
recursive=1
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
printf "clean: erase backup files in the given directories.\n\n"
|
||||
printf "Usage: clean [option] [directory1] [...[directoryX]]\n"
|
||||
printf "Options:\n"
|
||||
printf "\t-h, --help\t\tDisplay that help screen\n"
|
||||
printf "\t-r, --recurs\t\tDo a recursive cleaning\n"
|
||||
printf "\t-f, --force\t\tDo not ask for confirmation (use with care)\n"
|
||||
printf "\t-s, --shell\t\tDo nothing and display what will be executed\n"
|
||||
printf "\n"
|
||||
return 0
|
||||
;;
|
||||
-s|--shell)
|
||||
outshell=1
|
||||
shift
|
||||
;;
|
||||
-f|--force)
|
||||
force=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid parameter, use \"clean --help\" to display options list"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Handle remaining arguments as directories
|
||||
local dirlist=("$@")
|
||||
[[ ${#dirlist[@]} -eq 0 ]] && dirlist=(".")
|
||||
|
||||
local findopt=() rmopt=()
|
||||
(( ! recursive )) && findopt=(-maxdepth 1)
|
||||
(( ! force )) && rmopt=(-i)
|
||||
|
||||
for dir in "${dirlist[@]}"; do
|
||||
find "$dir" "${findopt[@]}" -type f \( -name "*~" -o -name "#*#" -o -name "*.bak" -o -name ".~*#" \) -print0 |
|
||||
while IFS= read -r -d '' f; do
|
||||
if (( outshell )); then
|
||||
if (( ${#rmopt[@]} )); then
|
||||
printf 'rm %s -- "%s"\n' "${rmopt[*]}" "$f"
|
||||
else
|
||||
printf 'rm -- "%s"\n' "$f"
|
||||
fi
|
||||
else
|
||||
rm "${rmopt[@]}" -- "$f"
|
||||
fi
|
||||
done
|
||||
done
|
||||
unset outshell dirlist dellist findopt rmopt
|
||||
}
|
||||
export -f clean
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Create a directory then goes inside
|
||||
# ------------------------------------------------------------------------------
|
||||
# Usage: mcd <directory>
|
||||
mcd()
|
||||
{
|
||||
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 "Create a directory then goes inside."
|
||||
disp E "Usage: mcd <directory>"
|
||||
disp E "Missing parameter. Use \"mcd --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
if ! mkdir -pv "$1"; then
|
||||
disp E "Failed to create directory \"$1\"."
|
||||
return 1
|
||||
fi
|
||||
if ! cd "$1"; then
|
||||
disp E "Failed to change to directory \"$1\"."
|
||||
return 1
|
||||
fi
|
||||
mkdir -pv "$1" && cd "$1" || echo "Failed create or change directory."
|
||||
}
|
||||
export -f mcd
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Rename all files in current directory to replace spaces with _
|
||||
# ------------------------------------------------------------------------------
|
||||
# Rename files and directories to replace spaces with another character
|
||||
# Usage: rmspc [options]
|
||||
# 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 more details (recursive mode only)"
|
||||
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
|
||||
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' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"rmspc --help\" to display usage."
|
||||
return 1
|
||||
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
|
||||
-c|--subst-char)
|
||||
substchar_set=1
|
||||
substchar="$2"
|
||||
shift 2
|
||||
;;
|
||||
|
||||
"-v" | "--verbose")
|
||||
local verb=1
|
||||
-v|--verbose)
|
||||
verb=1
|
||||
shift
|
||||
;;
|
||||
|
||||
"-s" | "--shell")
|
||||
local shell=1
|
||||
-s|--shell)
|
||||
shell=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
|
||||
*)
|
||||
disp E "Invalid parameter, use \"rmspc --help\" to display options list"
|
||||
echo
|
||||
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 ]] && disp I "Entering directory $(pwd)/$f ..."
|
||||
if (( recurs )) && [[ -d "$f" ]]; then
|
||||
(
|
||||
local lastdir=$f
|
||||
pushd "$f" >/dev/null
|
||||
rmspc $@
|
||||
popd >/dev/null
|
||||
[[ $verb ]] && disp I "Leaving directory $(pwd)/$lastdir"
|
||||
unset lastdir
|
||||
)
|
||||
(( verb )) && disp I "Entering directory $(pwd)/$f ..."
|
||||
pushd "$f" >/dev/null || return 1
|
||||
|
||||
if [[ $(echo $f | grep " ") ]]; then
|
||||
local newf="${f// /${substchar}}"
|
||||
local command="mv $mvopt \"$f\" \"$newf\""
|
||||
if [[ $shell ]]; then
|
||||
echo $command
|
||||
if (( substchar_set )); then
|
||||
rmspc ${recurs:+-r} -c "$substchar" ${verb:+-v} ${shell:+-s}
|
||||
else
|
||||
$command
|
||||
rmspc ${recurs:+-r} ${verb:+-v} ${shell:+-s}
|
||||
fi
|
||||
|
||||
popd >/dev/null || return 1
|
||||
(( verb )) && disp I "Leaving directory $(pwd)/$lastdir"
|
||||
)
|
||||
fi
|
||||
|
||||
if [[ "$f" == *" "* ]]; then
|
||||
local newf="${f// /${substchar}}"
|
||||
[[ "$f" == "$newf" ]] && continue
|
||||
if (( shell )); then
|
||||
if (( ${#mvopt[@]} )); then
|
||||
printf 'mv %s -- "%s" "%s"\n' "${mvopt[*]}" "$f" "$newf"
|
||||
else
|
||||
printf 'mv -- "%s" "%s"\n' "$f" "$newf"
|
||||
fi
|
||||
else
|
||||
mv "${mvopt[@]}" -- "$f" "$newf" || {
|
||||
disp E "Failed renaming \"$f\" to \"$newf\"."
|
||||
continue
|
||||
}
|
||||
fi
|
||||
fi
|
||||
done
|
||||
unset lst substchar verb shell newf command mvopt
|
||||
shopt -u nullglob
|
||||
}
|
||||
export -f rmspc
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# display stats about a file structure
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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 OPTIND opt
|
||||
|
||||
# Analyse options
|
||||
while [[ "$1" =~ ^- ]]; do
|
||||
local PARSED
|
||||
# Short: H, d, m, M, c, t, a, x:, X:
|
||||
# Long: human, details, average, median, count, total, all, ext:, ext-list:, min:, max:, help
|
||||
PARSED=$(getopt -o HdmMctax:X:h --long human,details,average,median,count,total,all,ext:,ext-list:,min:,max:,help -n 'file_stats' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"file_stats --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
eval set -- "$PARSED"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-H) human=1 ;;
|
||||
-d) details=1 ;;
|
||||
-m) only_avg=1; show_all=0 ;;
|
||||
-M) only_med=1; show_all=0 ;;
|
||||
-c) only_count=1; show_all=0 ;;
|
||||
-t) only_total=1; show_all=0 ;;
|
||||
-a) human=1; details=1 ;;
|
||||
-x) ext_filter="${2#.}"; shift ;;
|
||||
-X) ext_list="${2}"; shift ;;
|
||||
--min) min_size="$2"; shift ;;
|
||||
--max) max_size="$2"; shift ;;
|
||||
--) shift; break ;;
|
||||
-*) echo "Usage: file_stats [-h] [-d] [-mMctaxX --min N --max N] [path]"; return 1 ;;
|
||||
esac
|
||||
-h|--help)
|
||||
printf "Usage: file_stats [options] [path]\n\n"
|
||||
printf "Options:\n"
|
||||
printf "\t-H, --human\t\tHuman readable sizes\n"
|
||||
printf "\t-d, --details\t\tShow detailed histogram\n"
|
||||
printf "\t-m, --average\t\tShow only average size\n"
|
||||
printf "\t-M, --median\t\tShow only median size\n"
|
||||
printf "\t-c, --count\t\tShow only file count\n"
|
||||
printf "\t-t, --total\t\tShow only total size\n"
|
||||
printf "\t-a, --all\t\tShow all (human + details)\n"
|
||||
printf "\t-x, --ext [ext]\t\tFilter by extension\n"
|
||||
printf "\t-X, --ext-list [list]\tFilter by comma-separated list\n"
|
||||
printf "\t--min [size]\t\tMinimum size (e.g., 10M)\n"
|
||||
printf "\t--max [size]\t\tMaximum size (e.g., 100M)\n"
|
||||
return 0 ;;
|
||||
-H|--human)
|
||||
human=1
|
||||
shift
|
||||
;;
|
||||
-d|--details)
|
||||
details=1
|
||||
shift
|
||||
;;
|
||||
-m|--average)
|
||||
only_avg=1
|
||||
show_all=0
|
||||
shift
|
||||
;;
|
||||
-M|--median)
|
||||
only_med=1
|
||||
show_all=0
|
||||
shift
|
||||
;;
|
||||
-c|--count)
|
||||
only_count=1
|
||||
show_all=0
|
||||
shift
|
||||
;;
|
||||
-t|--total)
|
||||
only_total=1
|
||||
show_all=0
|
||||
shift
|
||||
;;
|
||||
-a|--all)
|
||||
human=1
|
||||
details=1
|
||||
shift
|
||||
;;
|
||||
-x|--ext)
|
||||
ext_filter="${2#.}"
|
||||
shift 2
|
||||
;;
|
||||
-X|--ext-list)
|
||||
ext_list="$2"
|
||||
shift 2
|
||||
;;
|
||||
--min)
|
||||
min_size="$2"
|
||||
shift 2
|
||||
;;
|
||||
--max)
|
||||
max_size="$2"
|
||||
shift 2
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid option: $1"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ -n "$1" ] && path="$1"
|
||||
[[ -n "$1" ]] && path="$1"
|
||||
|
||||
# Prepare find filters
|
||||
local find_cmd=(find "$path" -type f)
|
||||
|
||||
# Extension simple
|
||||
# Single extension filter
|
||||
if [[ -n "$ext_filter" ]]; then
|
||||
find_cmd+=(-iname "*.$ext_filter")
|
||||
fi
|
||||
|
||||
# Extension liste
|
||||
# Extension list filter
|
||||
if [[ -n "$ext_list" ]]; then
|
||||
IFS=',' read -ra exts <<< "$ext_list"
|
||||
find_cmd+=('(')
|
||||
@@ -262,7 +470,7 @@ file_stats()
|
||||
find_cmd+=(')')
|
||||
fi
|
||||
|
||||
# Taille min/max (à évaluer en octets)
|
||||
# Minimum/maximum size filters (evaluated in bytes)
|
||||
if [[ -n "$min_size" ]]; then
|
||||
find_cmd+=(-size +"$(numfmt --from=iec "$min_size")"c)
|
||||
fi
|
||||
@@ -270,13 +478,28 @@ file_stats()
|
||||
find_cmd+=(-size -"$(( $(numfmt --from=iec "$max_size") + 1 ))"c)
|
||||
fi
|
||||
|
||||
# Exécution
|
||||
"${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" '
|
||||
# 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)
|
||||
for (i=1; x>=1024 && i<5; i++) x /= 1024
|
||||
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
|
||||
@@ -288,50 +511,56 @@ file_stats()
|
||||
bucket[b]++
|
||||
}
|
||||
}
|
||||
|
||||
END {
|
||||
count = NR
|
||||
if (count == 0) {
|
||||
print "Aucun fichier trouvé."; exit
|
||||
print "No files found."
|
||||
exit
|
||||
}
|
||||
|
||||
moyenne = total / count
|
||||
if (count % 2 == 1)
|
||||
mediane = sizes[(count + 1) / 2]
|
||||
else
|
||||
mediane = (sizes[count / 2] + sizes[count / 2 + 1]) / 2
|
||||
average = total / count
|
||||
|
||||
function out(label, val) {
|
||||
if (human) val = human_readable(val)
|
||||
printf "%-20s : %s\n", label, val
|
||||
# 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("Taille moyenne", moyenne)
|
||||
else if (only_med) out("Taille médiane", mediane)
|
||||
else if (only_count) printf "Nombre de fichiers : %d\n", count
|
||||
else if (only_total) out("Taille totale", total)
|
||||
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 "Statistiques sur \"%s\"\n", path
|
||||
printf "Statistics for \"%s\"\n", path
|
||||
printf "-------------------------\n"
|
||||
}
|
||||
out("Nombre de fichiers", count)
|
||||
out("Taille totale", total)
|
||||
out("Taille moyenne", moyenne)
|
||||
out("Taille médiane", mediane)
|
||||
out("Taille minimale", min)
|
||||
out("Taille maximale", max)
|
||||
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 "\nHistogramme des tailles :"
|
||||
for (i = 0; i in bucket; i++) {
|
||||
low = 2^i
|
||||
high = 2^(i+1)
|
||||
if (i == 0)
|
||||
label = sprintf("%4s – %4s", "0", "1K")
|
||||
else
|
||||
label = sprintf("%4s – %4s", human_readable(low), human_readable(high))
|
||||
printf "%-20s : %5d fichiers\n", label, bucket[i]
|
||||
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]
|
||||
}
|
||||
}
|
||||
}'
|
||||
@@ -341,4 +570,236 @@ export -f file_stats
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Find the biggest files in a directory tree
|
||||
# Usage: findbig [options] [directory]
|
||||
# Options:
|
||||
# -h : display help screen
|
||||
# -d : display details (ls -l) for each file
|
||||
# -x : do not cross filesystem boundaries
|
||||
# -l : limit : number of files to return (default is 10)
|
||||
findbig()
|
||||
{
|
||||
local details=0 limit=${FINDBIG_DEFAULT_LIMIT:-10} one_fs=0
|
||||
|
||||
local PARSED
|
||||
PARSED=$(getopt -o hdl:x --long help,details,limit:,one-fs -n 'findbig' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"findbig --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
eval set -- "$PARSED"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "findbig: Find the N biggest files in a directory tree.\n\n"
|
||||
printf "Usage: findbig [options] [directory]\n\n"
|
||||
printf "Options:\n"
|
||||
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||
printf "\t-d, --details\t\tShow detailed file info (ls -ld)\n"
|
||||
printf "\t-l, --limit N\t\tNumber of files to return (default: 10)\n"
|
||||
printf "\t-x, --one-fs\t\tDo not cross filesystem boundaries\n"
|
||||
return 0
|
||||
;;
|
||||
-d|--details)
|
||||
details=1
|
||||
shift
|
||||
;;
|
||||
-l|--limit)
|
||||
limit="$2"
|
||||
[[ "$limit" =~ ^[0-9]+$ ]] || {
|
||||
disp E "Invalid limit: must be a positive integer."
|
||||
return 1
|
||||
}
|
||||
shift 2
|
||||
;;
|
||||
-x|--one-fs)
|
||||
one_fs=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid option: $1"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
local dir="${1:-.}"
|
||||
|
||||
# Prepare find arguments in an array for cleaner handling
|
||||
local find_args=(-L "$dir")
|
||||
(( one_fs )) && find_args+=(-xdev)
|
||||
find_args+=(-type f)
|
||||
|
||||
# Logic: find files, print size and path, sort numeric reverse, take N
|
||||
if (( details )); then
|
||||
find "${find_args[@]}" -printf "%s %p\n" 2>/dev/null | sort -rn | head -n "$limit" |
|
||||
while IFS= read -r line; do
|
||||
local path="${line#* }"
|
||||
ls -ld -- "$path"
|
||||
done
|
||||
else
|
||||
find "${find_args[@]}" -printf "%s %p\n" 2>/dev/null | sort -rn | head -n "$limit"
|
||||
fi
|
||||
}
|
||||
export -f findbig
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Find empty files in a directory tree
|
||||
# Usage: findzero [options] [directory]
|
||||
# Options:
|
||||
# -h : display help screen
|
||||
# -d : display details (ls -l) for each file
|
||||
# -x : do not cross filesystem boundaries
|
||||
# --delete : delete empty files and display their paths
|
||||
findzero()
|
||||
{
|
||||
local delete=0 details=0 one_fs=0
|
||||
|
||||
local PARSED
|
||||
# o: options, long: long equivalents
|
||||
PARSED=$(getopt -o hdx --long help,details,one-fs,delete -n 'findzero' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"findzero --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
eval set -- "$PARSED"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "findzero: Find or delete empty files in a directory tree.\n\n"
|
||||
printf "Usage: findzero [options] [directory]\n\n"
|
||||
printf "Options:\n"
|
||||
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||
printf "\t-d, --details\t\tShow detailed file info (ls -ls)\n"
|
||||
printf "\t-x, --one-fs\t\tDo not cross filesystem boundaries\n"
|
||||
printf "\t--delete\t\tActually remove the empty files\n"
|
||||
return 0 ;;
|
||||
-d|--details)
|
||||
details=1
|
||||
shift
|
||||
;;
|
||||
-x|--one-fs)
|
||||
one_fs=1
|
||||
shift
|
||||
;;
|
||||
--delete)
|
||||
delete=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid option: $1"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
local dir="${1:-.}"
|
||||
local find_args=("-L" "$dir" "-type" "f" "-empty")
|
||||
|
||||
(( one_fs )) && find_args+=("-xdev")
|
||||
|
||||
# Execution logic
|
||||
if (( delete )); then
|
||||
disp W "Deleting empty files in $dir..."
|
||||
find "${find_args[@]}" -delete -print
|
||||
elif (( details )); then
|
||||
find "${find_args[@]}" -ls
|
||||
else
|
||||
find "${find_args[@]}"
|
||||
fi
|
||||
}
|
||||
export -f findzero
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Find dead symbolic links in a directory tree
|
||||
# Usage: finddead [options] [directory]
|
||||
# Options:
|
||||
# -h : display help screen
|
||||
# -d : display details (ls -l) for each link
|
||||
# -x : do not cross filesystem boundaries
|
||||
# --delete : delete dead links and display their paths
|
||||
finddead()
|
||||
{
|
||||
local delete=0 details=0 one_fs=0
|
||||
|
||||
local PARSED
|
||||
PARSED=$(getopt -o hdx --long help,details,one-fs,delete -n 'finddead' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"finddead --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
eval set -- "$PARSED"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "finddead: Find or delete dead/broken symbolic links.\n\n"
|
||||
printf "Usage: finddead [options] [directory]\n\n"
|
||||
printf "Options:\n"
|
||||
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||
printf "\t-d, --details\t\tShow detailed symlink info (ls -ls)\n"
|
||||
printf "\t-x, --one-fs\t\tDo not cross filesystem boundaries\n"
|
||||
printf "\t--delete\t\tActually remove the dead links\n"
|
||||
return 0 ;;
|
||||
-d|--details)
|
||||
details=1
|
||||
shift
|
||||
;;
|
||||
-x|--one-fs)
|
||||
one_fs=1
|
||||
shift
|
||||
;;
|
||||
--delete)
|
||||
delete=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid option: $1"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
local dir="${1:-.}"
|
||||
# -xtype l searches for links that do not point to an existing file
|
||||
local find_args=("$dir" "-xtype" "l")
|
||||
(( one_fs )) && find_args+=("-xdev")
|
||||
|
||||
# Execution logic
|
||||
if (( delete )); then
|
||||
disp W "Deleting dead symlinks in $dir..."
|
||||
find "${find_args[@]}" -delete -print
|
||||
elif (( details )); then
|
||||
find "${find_args[@]}" -ls
|
||||
else
|
||||
find "${find_args[@]}"
|
||||
fi
|
||||
}
|
||||
export -f finddead
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
load_conf "filefct"
|
||||
|
||||
# EOF
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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,
|
||||
@@ -36,35 +36,81 @@
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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="ca fe"
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--delay=*)
|
||||
delay_ms="${arg#*=}"
|
||||
if ! [[ $delay_ms =~ ^[0-9]+$ ]]; then
|
||||
disp E "Invalid delay value, must be an integer (milliseconds)."
|
||||
local pattern="${BUSY_DEFAULT_PATTERN:-ca fe}" delay_ms="${BUSY_DEFAULT_DELAY:-0}"
|
||||
|
||||
local PARSED
|
||||
# Short: h, p:, d:
|
||||
# Long: help, pattern:, delay:
|
||||
PARSED=$(getopt -o hp:d: --long help,pattern:,delay: -n 'busy' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"busy --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
eval set -- "$PARSED"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "busy: Monitor /dev/urandom for a specific pattern.\n\n"
|
||||
printf "Usage: busy [options] [pattern]\n\n"
|
||||
printf "Options:\n"
|
||||
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||
printf "\t-p, --pattern PATTERN\tHex pattern to search (default: \"ca fe\")\n"
|
||||
printf "\t-d, --delay MS\t\tDelay between matches in milliseconds\n"
|
||||
return 0
|
||||
;;
|
||||
-p|--pattern)
|
||||
pattern="$2"
|
||||
shift 2
|
||||
;;
|
||||
-d|--delay)
|
||||
delay_ms="$2"
|
||||
if ! [[ "$delay_ms" =~ ^[0-9]+$ ]]; then
|
||||
disp E "Invalid delay: must be an integer (milliseconds)."
|
||||
return 1
|
||||
fi
|
||||
shift 2
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
pattern="$arg"
|
||||
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 }")
|
||||
local delay_s
|
||||
delay_s=$(awk "BEGIN{
|
||||
printf \"%.3f\", $delay_ms / 1000 }")
|
||||
|
||||
cat /dev/urandom | hexdump -C | grep --line-buffered "$pattern" | \
|
||||
# Monitor /dev/urandom
|
||||
(
|
||||
hexdump -C < /dev/urandom | grep -iF --line-buffered "$pattern" | \
|
||||
while read -r line; do
|
||||
echo $line
|
||||
echo "$line"
|
||||
[[ $delay_ms -gt 0 ]] && sleep "$delay_s"
|
||||
done
|
||||
unset pattern
|
||||
}
|
||||
) & 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
|
||||
|
||||
613
profile.d/git.sh
Executable file
613
profile.d/git.sh
Executable file
@@ -0,0 +1,613 @@
|
||||
#!/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.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Built-in defaults (can be overridden from [git] section in profile.conf)
|
||||
: "${GIT_MAIN_BRANCH:=main}"
|
||||
: "${GIT_DEFAULT_REMOTE:=origin}"
|
||||
: "${GIT_WIP_PREFIX:=wip}"
|
||||
: "${GIT_GACP_AUTO_ADD:=1}"
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Internal helper: ensure git is available and cwd is a git worktree
|
||||
_git_require_repo()
|
||||
{
|
||||
if ! command -v git >/dev/null 2>&1; then
|
||||
disp E "git command not found."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||
disp E "Current directory is not inside a git repository."
|
||||
return 1
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Internal helper: return default branch from remote HEAD, fallback to config
|
||||
_git_default_branch()
|
||||
{
|
||||
local remote="${1:-$GIT_DEFAULT_REMOTE}"
|
||||
local head
|
||||
|
||||
head=$(git symbolic-ref --quiet --short "refs/remotes/${remote}/HEAD" 2>/dev/null) || true
|
||||
if [[ -n $head ]]; then
|
||||
printf "%s\n" "${head#${remote}/}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf "%s\n" "$GIT_MAIN_BRANCH"
|
||||
return 0
|
||||
}
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Display compact git status + branch tracking information
|
||||
# Usage: gst [path]
|
||||
gst()
|
||||
{
|
||||
local PARSED
|
||||
PARSED=$(getopt -o h --long help -n 'gst' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"gst --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
|
||||
eval set -- "$PARSED"
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "gst: Display short git status and branch tracking info.\n"
|
||||
printf "Usage: gst [path]\n"
|
||||
return 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid options, use \"gst --help\" to display usage."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
local target="${1:-.}"
|
||||
git -C "$target" status --short --branch
|
||||
}
|
||||
export -f gst
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Show a readable commit graph
|
||||
# Usage: ggraph [-n limit]
|
||||
ggraph()
|
||||
{
|
||||
local PARSED
|
||||
PARSED=$(getopt -o hn: --long help,limit: -n 'ggraph' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"ggraph --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
|
||||
eval set -- "$PARSED"
|
||||
local limit=30
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "ggraph: Display decorated git history graph.\n"
|
||||
printf "Usage: ggraph [-n limit]\n"
|
||||
printf "Options:\n"
|
||||
printf "\t-n, --limit\tNumber of commits to display (default: 30)\n"
|
||||
return 0
|
||||
;;
|
||||
-n|--limit)
|
||||
limit="$2"
|
||||
shift 2
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid options, use \"ggraph --help\" to display usage."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ $limit =~ ^[0-9]+$ ]] || {
|
||||
disp E "Invalid limit: must be a positive integer."
|
||||
return 1
|
||||
}
|
||||
|
||||
_git_require_repo || return 1
|
||||
git log --graph --decorate --oneline --all --max-count="$limit"
|
||||
}
|
||||
export -f ggraph
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Sync current branch with remote (fetch + rebase)
|
||||
# Usage: gsync [remote]
|
||||
gsync()
|
||||
{
|
||||
local PARSED
|
||||
PARSED=$(getopt -o h --long help -n 'gsync' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"gsync --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
|
||||
eval set -- "$PARSED"
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "gsync: Fetch and rebase current branch onto its remote tracking branch.\n"
|
||||
printf "Usage: gsync [remote]\n"
|
||||
return 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid options, use \"gsync --help\" to display usage."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
_git_require_repo || return 1
|
||||
|
||||
local remote="${1:-$GIT_DEFAULT_REMOTE}"
|
||||
local branch upstream
|
||||
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || return 1
|
||||
upstream=$(git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null) || true
|
||||
|
||||
disp I "Fetching from $remote..."
|
||||
git fetch --prune "$remote" || return 1
|
||||
|
||||
if [[ -z $upstream ]]; then
|
||||
disp W "No upstream configured for $branch, skipping rebase."
|
||||
disp I "Set one with: git branch --set-upstream-to ${remote}/${branch} ${branch}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
disp I "Rebasing $branch onto $upstream..."
|
||||
git rebase "$upstream"
|
||||
}
|
||||
export -f gsync
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Add, commit, and push changes with automatic pull/rebase if needed
|
||||
# Usage: gacp -m "message" [file1 file2 ...]
|
||||
gacp()
|
||||
{
|
||||
local PARSED
|
||||
PARSED=$(getopt -o ham: --long help,auto,message: -n 'gacp' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"gacp --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
|
||||
eval set -- "$PARSED"
|
||||
local msg="" auto_add="$GIT_GACP_AUTO_ADD"
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "gacp: Run git add, git commit, and git push in one command.\n"
|
||||
printf "Usage: gacp [-a] -m \"message\" [file1 file2 ...]\n"
|
||||
printf "Options:\n"
|
||||
printf "\t-a, --auto\tAutomatically add all modified files (git add -A)\n"
|
||||
printf "\t-m, --message\tCommit message (mandatory)\n"
|
||||
printf "\n"
|
||||
printf "If files are provided, only those paths are added (-a is ignored).\n"
|
||||
printf "If no file is provided and -a is active, all changes are added with git add -A.\n"
|
||||
printf "Default for -a can be set via GIT_GACP_AUTO_ADD in profile.conf.\n"
|
||||
printf "If the remote branch moved forward, gacp pulls with rebase before pushing.\n"
|
||||
return 0
|
||||
;;
|
||||
-a|--auto)
|
||||
auto_add=1
|
||||
shift
|
||||
;;
|
||||
-m|--message)
|
||||
msg="$2"
|
||||
shift 2
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid options, use \"gacp --help\" to display usage."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
_git_require_repo || return 1
|
||||
|
||||
if [[ -z $msg ]]; then
|
||||
disp E "Missing commit message. Use -m or --message."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local branch upstream remote tracking_branch behind counts
|
||||
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || return 1
|
||||
upstream=$(git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null) || true
|
||||
|
||||
if [[ $# -gt 0 ]]; then
|
||||
auto_add=0
|
||||
disp I "Adding selected paths..."
|
||||
git add -- "$@" || return 1
|
||||
elif [[ $auto_add -eq 1 ]]; then
|
||||
disp I "Adding all changes..."
|
||||
git add -A || return 1
|
||||
else
|
||||
disp E "No files specified. Use -a/--auto or provide file paths."
|
||||
return 1
|
||||
fi
|
||||
|
||||
if git diff --cached --quiet; then
|
||||
disp W "No staged changes to commit."
|
||||
return 1
|
||||
fi
|
||||
|
||||
disp I "Creating commit..."
|
||||
git commit -m "$msg" || return 1
|
||||
|
||||
if [[ -n $upstream ]]; then
|
||||
remote="${upstream%%/*}"
|
||||
tracking_branch="${upstream#*/}"
|
||||
else
|
||||
remote="$GIT_DEFAULT_REMOTE"
|
||||
tracking_branch="$branch"
|
||||
fi
|
||||
|
||||
disp I "Fetching from $remote..."
|
||||
git fetch --prune "$remote" || return 1
|
||||
|
||||
if git rev-parse --verify --quiet "refs/remotes/${remote}/${tracking_branch}" >/dev/null; then
|
||||
counts=$(git rev-list --left-right --count HEAD..."${remote}/${tracking_branch}" 2>/dev/null) || return 1
|
||||
read -r _ behind <<< "$counts"
|
||||
if [[ ${behind:-0} -gt 0 ]]; then
|
||||
disp I "Remote branch is ahead, rebasing before push..."
|
||||
git pull --rebase "$remote" "$tracking_branch" || return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -n $upstream ]]; then
|
||||
disp I "Pushing to $upstream..."
|
||||
git push || return 1
|
||||
else
|
||||
disp I "Pushing and setting upstream to ${remote}/${branch}..."
|
||||
git push -u "$remote" "$branch" || return 1
|
||||
fi
|
||||
|
||||
disp I "gacp complete."
|
||||
return 0
|
||||
}
|
||||
export -f gacp
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Reset local branch to exact upstream state (stash local changes first)
|
||||
# Usage: greset [target]
|
||||
greset()
|
||||
{
|
||||
local PARSED
|
||||
PARSED=$(getopt -o hx --long help,with-ignored -n 'greset' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"greset --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
|
||||
eval set -- "$PARSED"
|
||||
local clean_ignored=0
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "greset: Reset current branch to upstream, stashing local changes first.\n"
|
||||
printf "Usage: greset [target]\n"
|
||||
printf "Options:\n"
|
||||
printf "\t-x, --with-ignored\tAlso remove ignored files (git clean -fdx)\n"
|
||||
printf "\n"
|
||||
printf "Default target is current branch upstream (@{u}).\n"
|
||||
printf "If no upstream exists, fallback target is <remote>/<branch>.\n"
|
||||
printf "This command stashes local modifications (tracked + untracked),\n"
|
||||
printf "drops local unpushed commits by hard-reset, and cleans untracked files.\n"
|
||||
return 0
|
||||
;;
|
||||
-x|--with-ignored)
|
||||
clean_ignored=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid options, use \"greset --help\" to display usage."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
_git_require_repo || return 1
|
||||
|
||||
local branch upstream target remote old_head stash_msg stash_out stash_created=0 dropped=0
|
||||
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || return 1
|
||||
upstream=$(git rev-parse --abbrev-ref --symbolic-full-name '@{u}' 2>/dev/null) || true
|
||||
target="$1"
|
||||
|
||||
if [[ -z $target ]]; then
|
||||
if [[ -n $upstream ]]; then
|
||||
target="$upstream"
|
||||
else
|
||||
remote="$GIT_DEFAULT_REMOTE"
|
||||
target="${remote}/${branch}"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z $remote ]]; then
|
||||
remote="${target%%/*}"
|
||||
fi
|
||||
|
||||
old_head=$(git rev-parse HEAD 2>/dev/null) || return 1
|
||||
|
||||
if ! git diff --quiet || ! git diff --cached --quiet || [[ -n $(git ls-files --others --exclude-standard) ]]; then
|
||||
stash_msg="greset:${branch}:$(date +'%Y-%m-%d %H:%M:%S')"
|
||||
disp I "Stashing local changes as '$stash_msg'..."
|
||||
stash_out=$(git stash push -u -m "$stash_msg" 2>&1) || {
|
||||
disp E "Failed to stash local changes."
|
||||
printf "%s\n" "$stash_out"
|
||||
return 1
|
||||
}
|
||||
[[ $stash_out != "No local changes to save"* ]] && stash_created=1
|
||||
fi
|
||||
|
||||
disp I "Fetching from $remote..."
|
||||
git fetch --prune "$remote" || return 1
|
||||
|
||||
if ! git rev-parse --verify --quiet "$target" >/dev/null; then
|
||||
disp E "Target '$target' does not exist."
|
||||
return 1
|
||||
fi
|
||||
|
||||
dropped=$(git rev-list --count "${target}..${old_head}" 2>/dev/null || printf "0")
|
||||
|
||||
disp W "Hard-resetting $branch to $target..."
|
||||
git reset --hard "$target" || return 1
|
||||
|
||||
if (( clean_ignored )); then
|
||||
git clean -fdx || return 1
|
||||
else
|
||||
git clean -fd || return 1
|
||||
fi
|
||||
|
||||
if (( stash_created )); then
|
||||
disp I "Local changes were stashed. Use 'git stash list' and 'git stash pop' when needed."
|
||||
fi
|
||||
disp I "greset complete. Dropped local-only commits: $dropped"
|
||||
return 0
|
||||
}
|
||||
export -f greset
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Create a quick WIP commit for local checkpointing
|
||||
# Usage: gwip [message]
|
||||
gwip()
|
||||
{
|
||||
local PARSED
|
||||
PARSED=$(getopt -o h --long help -n 'gwip' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"gwip --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
|
||||
eval set -- "$PARSED"
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "gwip: Create a local checkpoint commit with all tracked/untracked changes.\n"
|
||||
printf "Usage: gwip [message]\n"
|
||||
return 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid options, use \"gwip --help\" to display usage."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
_git_require_repo || return 1
|
||||
|
||||
local msg
|
||||
if [[ $# -gt 0 ]]; then
|
||||
msg="$*"
|
||||
else
|
||||
msg="$GIT_WIP_PREFIX: $(date +'%Y-%m-%d %H:%M:%S')"
|
||||
fi
|
||||
|
||||
git add -A || return 1
|
||||
git commit -m "$msg"
|
||||
}
|
||||
export -f gwip
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Delete merged local branches (except protected branches)
|
||||
# Usage: gprune [main-branch]
|
||||
gprune()
|
||||
{
|
||||
local PARSED
|
||||
PARSED=$(getopt -o h --long help -n 'gprune' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"gprune --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
|
||||
eval set -- "$PARSED"
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "gprune: Delete local branches already merged into main branch.\n"
|
||||
printf "Usage: gprune [main-branch]\n"
|
||||
return 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid options, use \"gprune --help\" to display usage."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
_git_require_repo || return 1
|
||||
|
||||
local base="${1:-$(_git_default_branch "$GIT_DEFAULT_REMOTE")}" current deleted=0
|
||||
current=$(git rev-parse --abbrev-ref HEAD 2>/dev/null) || return 1
|
||||
|
||||
disp I "Pruning branches merged into $base..."
|
||||
|
||||
while IFS= read -r b; do
|
||||
[[ -z $b ]] && continue
|
||||
[[ $b == "$current" ]] && continue
|
||||
[[ $b == "$base" ]] && continue
|
||||
[[ $b == "master" || $b == "main" || $b == "develop" || $b == "dev" ]] && continue
|
||||
git branch -d "$b" >/dev/null 2>&1 && {
|
||||
printf "Deleted: %s\n" "$b"
|
||||
((deleted++))
|
||||
}
|
||||
done < <(git branch --merged "$base" | sed -E 's/^\*?\s*//')
|
||||
|
||||
(( deleted == 0 )) && disp I "No merged branches to delete."
|
||||
}
|
||||
export -f gprune
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Print repository root path
|
||||
# Usage: groot
|
||||
groot()
|
||||
{
|
||||
local PARSED
|
||||
PARSED=$(getopt -o hg --long help,go -n 'groot' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"groot --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
|
||||
eval set -- "$PARSED"
|
||||
local do_go=0
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "groot: Display the absolute path of the current repository root.\n"
|
||||
printf "Usage: groot [-g|--go]\n"
|
||||
printf "Options:\n"
|
||||
printf "\t-g, --go\tChange current directory to repository root\n"
|
||||
return 0
|
||||
;;
|
||||
-g|--go)
|
||||
do_go=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid options, use \"groot --help\" to display usage."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
_git_require_repo || return 1
|
||||
local root
|
||||
root=$(git rev-parse --show-toplevel) || return 1
|
||||
|
||||
if (( do_go )); then
|
||||
cd "$root" || {
|
||||
disp E "Failed to move to repository root: $root"
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf "%s\n" "$root"
|
||||
}
|
||||
export -f groot
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
load_conf git
|
||||
|
||||
# EOF
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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,
|
||||
@@ -36,38 +36,76 @@
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Display list of commands and general informations
|
||||
# ------------------------------------------------------------------------------
|
||||
# Usage: help [command]
|
||||
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
|
||||
ppn Display process matching the exact process name given in parameter
|
||||
ppu Display processes owned by the given user
|
||||
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
|
||||
# If a command name is given, delegate to its --help output.
|
||||
if [[ $# -gt 0 && "$1" != "--help" && "$1" != "-h" ]]; then
|
||||
local cmd="$1"
|
||||
if declare -F "$cmd" >/dev/null 2>&1 || command -v "$cmd" >/dev/null 2>&1; then
|
||||
"$cmd" --help
|
||||
else
|
||||
disp E "Unknown command: $cmd"
|
||||
return 1
|
||||
fi
|
||||
return
|
||||
fi
|
||||
|
||||
Please use <command> --help to obtain usage details.
|
||||
EOF
|
||||
# shellcheck disable=SC2154 # color code in disp.sh
|
||||
# shellcheck disable=SC2059 # printf format is a color variable
|
||||
printf "${BIWhite}Welcome to your profile! Here is a list of available commands:${DEFAULTCOL}\n\n"
|
||||
printf "busy\t\tMonitor /dev/urandom for a hex pattern — look busy\n"
|
||||
printf "check_updates\tCheck for new versions of profile\n"
|
||||
printf "clean\t\tErase backup files in given directories, optionally recursive\n"
|
||||
printf "disp\t\tDisplay formatted info/warning/error/debug messages\n"
|
||||
printf "dwl\t\tDownload a URL using curl, wget, or fetch transparently\n"
|
||||
printf "expandlist\tExpand glob expressions into a quoted, separated list\n"
|
||||
printf "file_stats\tDisplay file size statistics for a path\n"
|
||||
printf "findbig\t\tFind the biggest files in the given or current directory\n"
|
||||
printf "finddead\tFind dead symbolic links in the given or current directory\n"
|
||||
printf "findzero\tFind empty files in the given or current directory\n"
|
||||
printf "gacp\t\tAdd, commit and push changes (auto-pull if needed)\n"
|
||||
printf "genpwd\t\tGenerate one or more random secure passwords with configurable constraints\n"
|
||||
printf "ggraph\t\tDisplay decorated git history graph\n"
|
||||
printf "gprune\t\tDelete local branches already merged into main branch\n"
|
||||
printf "greset\t\tReset branch to upstream (stash local, drop local commits)\n"
|
||||
printf "groot\t\tDisplay repository root path (or cd to it with -g)\n"
|
||||
printf "gsync\t\tFetch and rebase current branch onto upstream\n"
|
||||
printf "gst\t\tDisplay short git status and branch tracking info\n"
|
||||
printf "gwip\t\tCreate a quick WIP checkpoint commit\n"
|
||||
printf "gpid\t\tGive the list of PIDs matching the given process name(s)\n"
|
||||
printf "isipv4\t\tTell if the given parameter is a valid IPv4 address\n"
|
||||
printf "isipv6\t\tTell if the given parameter is a valid IPv6 address\n"
|
||||
printf "ku\t\tKill all processes owned by the given user name or ID\n"
|
||||
printf "matrix\t\tConsole screensaver with Matrix-style digital rain (binary, kana, ascii charset)\n"
|
||||
printf "mcd\t\tCreate a directory and immediately move into it\n"
|
||||
printf "meteo\t\tDisplay weather forecast for the configured or given city\n"
|
||||
printf "myextip\t\tGet information about your public IP address\n"
|
||||
printf "pkgs\t\tSearch for a pattern in installed package names (dpkg/rpm, supports -i)\n"
|
||||
printf "ppg\t\tLook for the given pattern in running processes\n"
|
||||
printf "ppn\t\tList processes matching an exact command name\n"
|
||||
printf "ppu\t\tList processes owned by a specific user\n"
|
||||
printf "profile_upgrade\tUpgrade profile to the latest version (git pull or archive)\n"
|
||||
printf "pwdscore\tCalculate the strength score of a given password\n"
|
||||
printf "rain\t\tConsole screensaver with falling-rain effect (multiple color themes)\n"
|
||||
printf "rmhost\t\tRemove host (name and IP) from SSH known_hosts; supports --all-users as root\n"
|
||||
printf "rmspc\t\tReplace spaces in filenames with underscores (or a custom character)\n"
|
||||
printf "setlocale\tSet console locale to any installed locale\n"
|
||||
printf " * setc\tSet locale to standard C (POSIX)\n"
|
||||
printf " * set*\tLocale shortcuts generated from SET_LOCALE in profile.conf\n"
|
||||
printf "settrace\tActivate or deactivate ERR trap to display backtrace on script errors\n"
|
||||
printf "set_theme\tSwitch the prompt colour theme; no argument lists available themes\n"
|
||||
printf "showinfo\tDisplay welcome banner and system information (figlet + neofetch/fastfetch)\n"
|
||||
printf "ssr\t\tSSH into a server as root, forwarding extra ssh options\n"
|
||||
printf "taz\t\tCompress files and directories into a chosen archive format\n"
|
||||
printf "urlencode\tURL-encode a string\n"
|
||||
printf "utaz\t\tSmartly uncompress archives (zip, tar.gz/bz2/xz/lz, rar, arj, lha, ace, 7z, zst, cpio, cab, deb, rpm)\n"
|
||||
printf "ver\t\tDisplay the installed profile version\n\n"
|
||||
|
||||
printf "\nPlease use <command> --help to obtain usage details.\n"
|
||||
}
|
||||
export -f help
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# EOF
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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,
|
||||
@@ -36,51 +36,144 @@
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Show profile version
|
||||
# ------------------------------------------------------------------------------
|
||||
# Usage: ver
|
||||
ver()
|
||||
{
|
||||
local PARSED
|
||||
|
||||
PARSED=$(getopt -o h --long help -n 'ver' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"ver --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
|
||||
eval set -- "$PARSED"
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "ver: Display the current profile version.\nUsage: ver\n"
|
||||
return 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid options, use \"ver --help\" to display usage."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -z $PROFVERSION ]] && \
|
||||
disp W "No version defined. Profile is probably badly installed." && \
|
||||
return 1
|
||||
disp "Profile version $PROFVERSION."
|
||||
}
|
||||
export -f ver
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Display weather of the given city (or default one)
|
||||
# ------------------------------------------------------------------------------
|
||||
# Display weather for the given city (or the default one)
|
||||
# Usage: meteo [city1 city2 ...]
|
||||
meteo()
|
||||
{
|
||||
local encoded cities=("$@")
|
||||
[[ $# -eq 0 ]] && cities=("$DEFAULT_CITY")
|
||||
local PARSED
|
||||
|
||||
PARSED=$(getopt -o h --long help -n 'meteo' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"meteo --help\" to display usage."
|
||||
return 1
|
||||
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")
|
||||
curl -s "https://wttr.in/$encoded" || \
|
||||
disp E "Failed fetching datas for $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"
|
||||
local PARSED
|
||||
|
||||
PARSED=$(getopt -o h --long help -n 'showinfo' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"showinfo --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
eval set -- "$PARSED"
|
||||
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "showinfo: Display system information (hostname, kernel, uptime and fetch output when available).\n"
|
||||
printf "Usage: showinfo\n"
|
||||
return 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid options, use \"showinfo --help\" to display usage."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
local hostname_str
|
||||
local figopt=()
|
||||
hostname_str="$(hostname)"
|
||||
|
||||
printf "\n"
|
||||
if command -v figlet >/dev/null 2>&1; then
|
||||
if [[ -s /usr/share/figlet/ansi_shadow.flf ]]; then
|
||||
local figopt="-f ansi_shadow"
|
||||
fi
|
||||
if [[ -n $figopt ]]; then
|
||||
figlet -k $figopt $(hostname)
|
||||
[[ -s /usr/share/figlet/ansi_shadow.flf ]] && \
|
||||
figopt=(-f ansi_shadow)
|
||||
figlet -k "${figopt[@]}" "$hostname_str"
|
||||
else
|
||||
figlet $(hostname)
|
||||
printf "%s\n" "$hostname_str"
|
||||
fi
|
||||
else
|
||||
hostname -f
|
||||
fi
|
||||
echo ""
|
||||
|
||||
printf "\n"
|
||||
if command -v neofetch >/dev/null 2>&1; then
|
||||
neofetch
|
||||
elif command -v fastfetch >/dev/null 2>&1; then
|
||||
@@ -90,15 +183,17 @@ showinfo()
|
||||
if [[ -s /etc/os-release ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
. /etc/os-release
|
||||
echo "$NAME $VERSION"
|
||||
printf "%s %s\n" "$NAME" "$VERSION"
|
||||
else
|
||||
cat /proc/version
|
||||
fi
|
||||
echo "Uptime: $(uptime -p)"
|
||||
printf "Uptime: %s\n" "$(uptime -p)"
|
||||
)
|
||||
fi
|
||||
}
|
||||
export -f showinfo
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
load_conf info
|
||||
# EOF
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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,
|
||||
@@ -34,7 +34,8 @@
|
||||
# * OF SUCH DAMAGE.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
locale_check() {
|
||||
locale_check()
|
||||
{
|
||||
locale -a | grep -qx "$1" || {
|
||||
disp W "Locale '$1' is not installed on this system."
|
||||
return 1
|
||||
@@ -45,9 +46,37 @@ locale_check() {
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Change locale to the given one in parameter
|
||||
# ------------------------------------------------------------------------------
|
||||
# Usage: setlocale <locale>
|
||||
setlocale()
|
||||
{
|
||||
local PARSED
|
||||
PARSED=$(getopt -o h --long help -n 'setlocale' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"setlocale --help\" to display usage."
|
||||
return 1
|
||||
fi
|
||||
eval set -- "$PARSED"
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "setlocale: Configure system environment locale variables.\n\n"
|
||||
printf "Usage: setlocale <locale>\n\n"
|
||||
printf "Options:\n"
|
||||
printf " -h, --help Display this help screen\n"
|
||||
return 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid options, use \"setlocale --help\" to display usage."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
local loc=$1
|
||||
[[ -z $loc ]] && disp E "No locale specified." && return 1
|
||||
|
||||
@@ -64,11 +93,12 @@ setlocale()
|
||||
disp I "Locale set to $loc."
|
||||
}
|
||||
export -f setlocale
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Special case : change locale to C standard
|
||||
# ------------------------------------------------------------------------------
|
||||
# Usage: setc
|
||||
setc()
|
||||
{
|
||||
# Locale definitions
|
||||
@@ -76,27 +106,77 @@ setc()
|
||||
disp I "Locale changed to standard C (POSIX)."
|
||||
}
|
||||
export -f setc
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Change locale to French
|
||||
# ------------------------------------------------------------------------------
|
||||
setfr()
|
||||
# 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()
|
||||
{
|
||||
# Set fr locale definitions
|
||||
setlocale "fr_FR.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 setfr
|
||||
|
||||
|
||||
export -f build_locale_shortcuts
|
||||
# ------------------------------------------------------------------------------
|
||||
# Change locale to US (needed by Steam)
|
||||
# ------------------------------------------------------------------------------
|
||||
setus()
|
||||
{
|
||||
setlocale "en_US.UTF-8"
|
||||
}
|
||||
export -f setus
|
||||
|
||||
|
||||
load_conf lang
|
||||
build_locale_shortcuts
|
||||
# ------------------------------------------------------------------------------
|
||||
# EOF
|
||||
|
||||
286
profile.d/net.sh
286
profile.d/net.sh
@@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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,
|
||||
@@ -35,38 +35,173 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Determine if parameter is a valid IPv4 address
|
||||
# Download a resource using curl, wget, or fetch.
|
||||
# Usage: dwl [-t <seconds>] <url> [output_file]
|
||||
dwl()
|
||||
{
|
||||
local timeout=""
|
||||
|
||||
# Parse leading options before the URL.
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--help|-h)
|
||||
echo "Usage: dwl [-t <seconds>|--timeout <seconds>] <url> [output_file]"
|
||||
echo "Downloads a resource using curl, wget, or fetch."
|
||||
echo ""
|
||||
echo "Arguments:"
|
||||
echo " -t, --timeout Maximum time in seconds to wait for the transfer."
|
||||
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
|
||||
;;
|
||||
-t|--timeout)
|
||||
[[ -z "${2:-}" || ! "${2:-}" =~ ^[0-9]+$ ]] && {
|
||||
echo "Error: --timeout requires a positive integer argument." >&2
|
||||
return 1
|
||||
}
|
||||
timeout="$2"
|
||||
shift 2
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
echo "Error: Unknown option '$1'. Try 'dwl --help'." >&2
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
case "${1:-}" in
|
||||
"")
|
||||
echo "Error: URL argument is missing." >&2
|
||||
echo "Try 'dwl --help' for usage." >&2
|
||||
return 1
|
||||
;;
|
||||
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()
|
||||
{
|
||||
local args=(-sL)
|
||||
[[ -n "$timeout" ]] && args+=(--max-time "$timeout" --connect-timeout "$timeout")
|
||||
if [[ -z "$output" ]]; then
|
||||
curl "${args[@]}" "$url"
|
||||
else
|
||||
curl "${args[@]}" -o "$output" "$url"
|
||||
fi
|
||||
}
|
||||
|
||||
_try_wget()
|
||||
{
|
||||
local args=(-q)
|
||||
[[ -n "$timeout" ]] && args+=(--timeout="$timeout")
|
||||
if [[ -z "$output" ]]; then
|
||||
wget "${args[@]}" -O- "$url"
|
||||
else
|
||||
wget "${args[@]}" -O "$output" "$url"
|
||||
fi
|
||||
}
|
||||
|
||||
_try_fetch()
|
||||
{
|
||||
local args=()
|
||||
[[ -n "$timeout" ]] && args+=(-T "$timeout")
|
||||
if [[ -z "$output" ]]; then
|
||||
fetch "${args[@]}" -o - "$url"
|
||||
else
|
||||
fetch "${args[@]}" -o "$output" "$url"
|
||||
fi
|
||||
}
|
||||
|
||||
if [[ -n "$preferred" ]]; then
|
||||
command -v "$preferred" >/dev/null 2>&1 || {
|
||||
echo "Error: preferred download tool '$preferred' is not installed." >&2
|
||||
return 1
|
||||
}
|
||||
case "$preferred" in
|
||||
curl) _try_curl ;;
|
||||
wget) _try_wget ;;
|
||||
fetch) _try_fetch ;;
|
||||
*)
|
||||
echo "Error: DWL_PREFERRED_TOOL '$preferred' is not supported (use curl, wget or fetch)." >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
elif command -v curl >/dev/null 2>&1; then
|
||||
_try_curl
|
||||
elif command -v wget >/dev/null 2>&1; then
|
||||
_try_wget
|
||||
elif command -v fetch >/dev/null 2>&1; then
|
||||
_try_fetch
|
||||
else
|
||||
echo "Error: No download utility (curl, wget, or fetch) found." >&2
|
||||
return 1
|
||||
fi
|
||||
unset -f _try_curl _try_wget _try_fetch
|
||||
}
|
||||
export -f dwl
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Determine if parameter is a valid IPv4 address
|
||||
# Usage: isipv4 <ip_address>
|
||||
isipv4()
|
||||
{
|
||||
# Set up local variables
|
||||
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
|
||||
# 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="."
|
||||
ip=($ip)
|
||||
IFS='.'
|
||||
read -r -a ip_arr <<< "$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
|
||||
disp "The given IPv4 is valid."
|
||||
|
||||
# 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
|
||||
fi
|
||||
if [[ -t 1 ]]; then
|
||||
disp "The given parameter is NOT a valid IPv4."
|
||||
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
|
||||
# ------------------------------------------------------------------------------
|
||||
# Determine if parameter is a valid IPv6 address
|
||||
# Usage: isipv6 <ip_address>
|
||||
isipv6()
|
||||
{
|
||||
local ip="$1"
|
||||
@@ -83,25 +218,136 @@ isipv6()
|
||||
return 1
|
||||
}
|
||||
export -f isipv6
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Encode a string so it can be used as a URL parameter
|
||||
# ------------------------------------------------------------------------------
|
||||
urlencode() {
|
||||
# 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 "$c" ;;
|
||||
[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,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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,
|
||||
@@ -35,49 +35,165 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Look for a package within installed one
|
||||
# ------------------------------------------------------------------------------
|
||||
pkgs()
|
||||
# Detect the active package manager of the current distribution.
|
||||
# Detection is based on /etc/os-release (ID / ID_LIKE), then falls back to
|
||||
# checking available binaries in a fixed priority order.
|
||||
# Echoes one of: apt dnf yum zypper pacman apk portage xbps nix
|
||||
# Returns 1 if no known package manager could be identified.
|
||||
get_pkgmgr()
|
||||
{
|
||||
local 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 distro_id="" distro_like=""
|
||||
if [[ -r /etc/os-release ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
distro_id=$( . /etc/os-release 2>/dev/null; printf '%s' "${ID:-}" )
|
||||
# shellcheck disable=SC1091
|
||||
distro_like=$( . /etc/os-release 2>/dev/null; printf '%s' "${ID_LIKE:-}" )
|
||||
fi
|
||||
|
||||
# Map distro IDs/families to a package manager.
|
||||
# ID_LIKE is space-separated and may list multiple families.
|
||||
local id
|
||||
for id in $distro_id $distro_like; do
|
||||
case "${id,,}" in
|
||||
debian|ubuntu|linuxmint|raspbian|pop|kali|elementary|zorin|neon|parrot)
|
||||
echo "apt"
|
||||
return 0
|
||||
;;
|
||||
|
||||
"-"*)
|
||||
disp E "Invalid option, use \"dpkgs --help\" to display usage."
|
||||
echo
|
||||
return 1
|
||||
fedora)
|
||||
echo "dnf"
|
||||
return 0
|
||||
;;
|
||||
|
||||
*)
|
||||
local pkg=$1 && shift
|
||||
count=$(($count + 1))
|
||||
[[ $count -gt 1 ]] &&
|
||||
disp E "Please specify a package name, without space, eventually partial." &&
|
||||
return 1
|
||||
|
||||
rhel|centos|rocky|almalinux|ol|scientific|amzn)
|
||||
command -v dnf >/dev/null 2>&1 && { echo "dnf"; return 0; }
|
||||
echo "yum"
|
||||
return 0
|
||||
;;
|
||||
opensuse*|sles|sled)
|
||||
echo "zypper"
|
||||
return 0
|
||||
;;
|
||||
arch|manjaro|endeavouros|garuda|artix|cachyos)
|
||||
echo "pacman"
|
||||
return 0
|
||||
;;
|
||||
alpine)
|
||||
echo "apk"
|
||||
return 0
|
||||
;;
|
||||
gentoo)
|
||||
echo "portage"
|
||||
return 0
|
||||
;;
|
||||
void)
|
||||
echo "xbps"
|
||||
return 0
|
||||
;;
|
||||
nixos)
|
||||
echo "nix"
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
[[ $count -lt 1 ]] &&
|
||||
disp E "Please specify a package name, without space, eventually partial." &&
|
||||
return 1
|
||||
|
||||
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
|
||||
fi
|
||||
$cmd | grep $pkg
|
||||
# Fallback: check for binaries in priority order.
|
||||
local bin
|
||||
for bin in apt apt-get dnf yum zypper pacman apk emerge xbps-install nix-env; do
|
||||
command -v "$bin" >/dev/null 2>&1 && {
|
||||
case "$bin" in
|
||||
apt-get) echo "apt" ;;
|
||||
emerge) echo "portage" ;;
|
||||
xbps-install) echo "xbps" ;;
|
||||
nix-env) echo "nix" ;;
|
||||
*) echo "$bin" ;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
export -f pkgs
|
||||
export -f get_pkgmgr
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Look for a package within installed one
|
||||
# Usage: pkgs <string>
|
||||
pkgs()
|
||||
{
|
||||
local ignore_case=${PKGS_DEFAULT_IGNORE_CASE:-0}
|
||||
|
||||
local PARSED
|
||||
PARSED=$(getopt -o hi --long help,ignore-case -n 'pkgs' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"pkgs --help\" to display usage."
|
||||
return 1
|
||||
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
|
||||
;;
|
||||
-i|--ignore-case)
|
||||
ignore_case=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid option: $1"
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
local pkg="$1"
|
||||
[[ -z "$pkg" ]] && {
|
||||
disp E "Please specify a package name, without space, eventually partial."
|
||||
return 1
|
||||
}
|
||||
|
||||
# Build grep command
|
||||
local grep_opt=""
|
||||
(( ignore_case )) && grep_opt="-i"
|
||||
|
||||
local pkgmgr
|
||||
pkgmgr=$(get_pkgmgr) || {
|
||||
disp E "No usable package manager could be detected on this system."
|
||||
return 2
|
||||
}
|
||||
|
||||
local -a list_cmd
|
||||
case "$pkgmgr" in
|
||||
apt) list_cmd=(dpkg-query -l) ;;
|
||||
dnf|yum|zypper) list_cmd=(rpm -qa) ;;
|
||||
pacman) list_cmd=(pacman -Q) ;;
|
||||
apk) list_cmd=(apk list --installed) ;;
|
||||
portage) list_cmd=(qlist -I) ;;
|
||||
xbps) list_cmd=(xbps-query -l) ;;
|
||||
nix) list_cmd=(nix-env -q) ;;
|
||||
*)
|
||||
disp E "Package manager '$pkgmgr' is not supported by pkgs."
|
||||
return 2
|
||||
;;
|
||||
esac
|
||||
|
||||
"${list_cmd[@]}" | grep ${grep_opt:+"$grep_opt"} "$pkg"
|
||||
}
|
||||
export -f pkgs
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
load_conf "packages"
|
||||
|
||||
# EOF
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# ------------------------------------------------------------------------------
|
||||
# Copyright (c) 2013-2022 Geoffray Levasseur <fatalerrors@geoffray-levasseur.org>
|
||||
# Protected by the BSD3 license. Please read bellow for details.
|
||||
@@ -36,18 +36,58 @@
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Search processes matching the given string
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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
|
||||
@@ -55,15 +95,24 @@ ppu()
|
||||
|
||||
# -u lists processes for a specific user
|
||||
# -o provides a clean, standard output format
|
||||
ps -u "$1" -o pid,user,%cpu,%mem,start,time,command
|
||||
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
|
||||
@@ -72,56 +121,136 @@ ppn()
|
||||
# -e: select all processes
|
||||
# -o: specify custom output columns (PID and Command name)
|
||||
# grep -w: ensures exact word matching so 'bash' doesn't match 'dbash'
|
||||
# shellcheck disable=SC2009 # pgrep do not offer the -w switch
|
||||
ps -eo pid,comm | grep -w "$1"
|
||||
}
|
||||
export -f ppn
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Get PID list of the given process name
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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 (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
|
||||
for u in "$@"; do
|
||||
if ! id "$u" >/dev/null 2>&1; then
|
||||
disp E "User '$u' does not exist."
|
||||
return 1
|
||||
else
|
||||
killall ${KU_DEFAULT_SIGNAL:+-${KU_DEFAULT_SIGNAL}} -u "$u"
|
||||
fi
|
||||
done
|
||||
}
|
||||
export -f ku
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Kill all children of a process then the process (kill tree)
|
||||
# ------------------------------------------------------------------------------
|
||||
# Usage: kt <pid> [kill_options]
|
||||
kt()
|
||||
{
|
||||
[[ -z $1 ]] && echo -e "Usage:\n\tkt <pid> [kill_options]"
|
||||
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
|
||||
printf "kt: Kill all children of a process then the process (kill tree).\n\n"
|
||||
printf "Usage: kt <pid> [kill_options]\n\n"
|
||||
printf "Options:\n"
|
||||
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||
return 0
|
||||
fi
|
||||
if [[ -z "$1" ]]; then
|
||||
disp E "Usage: kt <pid>"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local parent_pid="$1"
|
||||
shift
|
||||
if [[ "$parent_pid" == "0" || "$parent_pid" == "1" ]]; then
|
||||
disp E "Safety abort: Refusing to kill PID $parent_pid (system critical)."
|
||||
return 1
|
||||
fi
|
||||
|
||||
local children_pids
|
||||
children_pids=$(pgrep -P "$parent_pid")
|
||||
|
||||
for pid in $children_pids; do
|
||||
kt "$pid" "$@"
|
||||
kt "$pid" "$@" || break
|
||||
done
|
||||
kill "$@" "$parent_pid"
|
||||
}
|
||||
|
||||
export -f kt
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
load_conf "processes"
|
||||
|
||||
# EOF
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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,
|
||||
@@ -33,22 +34,225 @@
|
||||
# * OF SUCH DAMAGE.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Parse a prompt theme file safely — it is NEVER sourced or executed.
|
||||
# Two categories of keys are accepted:
|
||||
# PROMPT_COLOR_* — prompt slot colours (TIME_FG, BAR_BG, …)
|
||||
# Standard colour variables from disp.sh (Blue, On_IBlack, …) — allows a
|
||||
# theme to redefine the palette used everywhere in the shell session.
|
||||
# Allowed value forms:
|
||||
# $ColorName or ${ColorName} — colour variable from disp.sh (resolved by
|
||||
# indirection via ${!varname})
|
||||
# \e[...m or \033[...m — raw ANSI escape literal (single block)
|
||||
# Any other key or value is rejected with a warning.
|
||||
# Usage: load_theme <theme_name_or_path> [theme_dir]
|
||||
# theme_name_or_path : bare name (e.g. "dark") or an explicit path.
|
||||
# theme_dir : directory to search for bare names; defaults to
|
||||
# $MYPATH/profile.d/themes. Overridable via
|
||||
# PROMPT_THEME_DIR.
|
||||
load_theme()
|
||||
{
|
||||
local theme_name="$1"
|
||||
local theme_dir="${2:-${PROMPT_THEME_DIR:-$MYPATH/profile.d/themes}}"
|
||||
local theme_file=""
|
||||
|
||||
[[ -z "$theme_name" ]] && return 0
|
||||
|
||||
if [[ "$theme_name" == /* || "$theme_name" == */* ]]; then
|
||||
theme_file="$theme_name"
|
||||
else
|
||||
theme_file="$theme_dir/${theme_name}.theme"
|
||||
fi
|
||||
|
||||
if [[ ! -f "$theme_file" || ! -r "$theme_file" ]]; then
|
||||
printf "[ Warning ] load_theme: theme file not found: %s\n" "$theme_file" >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# ---- Key whitelist: prompt slots ----------------------------------------
|
||||
local -A _lth_allowed=(
|
||||
[PROMPT_COLOR_TIME_FG]=1 [PROMPT_COLOR_TIME_BG]=1
|
||||
[PROMPT_COLOR_BAR_BG]=1
|
||||
[PROMPT_COLOR_OK_FG]=1 [PROMPT_COLOR_OK_MARK]=1
|
||||
[PROMPT_COLOR_ERR_BG]=1 [PROMPT_COLOR_ERR_FG]=1 [PROMPT_COLOR_ERR_MARK]=1
|
||||
[PROMPT_COLOR_ROOT_FG]=1 [PROMPT_COLOR_USER_FG]=1
|
||||
[PROMPT_COLOR_DIR_FG]=1
|
||||
)
|
||||
|
||||
# ---- Colour variable names exported by disp.sh --------------------------
|
||||
local _lth_color_re
|
||||
_lth_color_re='Black|Red|Green|Yellow|Blue|Purple|Cyan|White'
|
||||
_lth_color_re+='|BBlack|BRed|BGreen|BYellow|BBlue|BPurple|BCyan|BWhite'
|
||||
_lth_color_re+='|UBlack|URed|UGreen|UYellow|UBlue|UPurple|UCyan|UWhite'
|
||||
_lth_color_re+='|On_Black|On_Red|On_Green|On_Yellow|On_Blue|On_Purple|On_Cyan|On_White'
|
||||
_lth_color_re+='|IBlack|IRed|IGreen|IYellow|IBlue|IPurple|ICyan|IWhite'
|
||||
_lth_color_re+='|BIBlack|BIRed|BIGreen|BIYellow|BIBlue|BIPurple|BICyan|BIWhite'
|
||||
_lth_color_re+='|On_IBlack|On_IRed|On_IGreen|On_IYellow|On_IBlue|On_IPurple|On_ICyan|On_IWhite'
|
||||
_lth_color_re+='|DEFAULTFG|DEFAULTBG|DEFAULTCOL|RESETCOL'
|
||||
|
||||
# ---- Key whitelist: standard colour vars (same list as above) -----------
|
||||
local _lth_cn
|
||||
for _lth_cn in \
|
||||
Black Red Green Yellow Blue Purple Cyan White \
|
||||
BBlack BRed BGreen BYellow BBlue BPurple BCyan BWhite \
|
||||
UBlack URed UGreen UYellow UBlue UPurple UCyan UWhite \
|
||||
On_Black On_Red On_Green On_Yellow On_Blue On_Purple On_Cyan On_White \
|
||||
IBlack IRed IGreen IYellow IBlue IPurple ICyan IWhite \
|
||||
BIBlack BIRed BIGreen BIYellow BIBlue BIPurple BICyan BIWhite \
|
||||
On_IBlack On_IRed On_IGreen On_IYellow On_IBlue On_IPurple On_ICyan On_IWhite \
|
||||
DEFAULTFG DEFAULTBG DEFAULTCOL RESETCOL; do
|
||||
_lth_allowed[$_lth_cn]=1
|
||||
done
|
||||
unset _lth_cn
|
||||
|
||||
# ERE: safe colour reference $Name or ${Name}
|
||||
local _lth_ref_re='^\$\{?('"$_lth_color_re"')\}?$'
|
||||
|
||||
# ERE: raw ANSI escape literal \e[...m or \033[...m
|
||||
local _lth_ansi_re='^(\\e|\\033)\[[0-9;]*m$'
|
||||
|
||||
# ---- Line parser ---------------------------------------------------------
|
||||
local _lth_line _lth_key _lth_value _lth_varname _lth_lineno=0
|
||||
|
||||
while IFS= read -r _lth_line || [[ -n "$_lth_line" ]]; do
|
||||
((_lth_lineno++))
|
||||
_lth_line="${_lth_line%$'\r'}" # strip CR
|
||||
_lth_line="${_lth_line#"${_lth_line%%[![:space:]]*}"}" # ltrim
|
||||
_lth_line="${_lth_line%"${_lth_line##*[![:space:]]}"}" # rtrim
|
||||
[[ -z "$_lth_line" || "$_lth_line" == '#'* ]] && continue # blank/comment
|
||||
[[ "$_lth_line" == 'export '* ]] && _lth_line="${_lth_line#export }" # strip prefix
|
||||
|
||||
if [[ "$_lth_line" != *=* ]]; then
|
||||
printf "[ Warning ] load_theme: %s:%d: not a key=value pair, ignoring.\n" \
|
||||
"$theme_file" "$_lth_lineno" >&2
|
||||
continue
|
||||
fi
|
||||
|
||||
_lth_key="${_lth_line%%=*}"
|
||||
_lth_value="${_lth_line#*=}"
|
||||
_lth_key="${_lth_key#"${_lth_key%%[![:space:]]*}"}"
|
||||
_lth_key="${_lth_key%"${_lth_key##*[![:space:]]}"}" # trim key
|
||||
|
||||
if [[ -z "${_lth_allowed[$_lth_key]+x}" ]]; then
|
||||
printf "[ Warning ] load_theme: %s:%d: key '%s' is not allowed, ignoring.\n" \
|
||||
"$theme_file" "$_lth_lineno" "$_lth_key" >&2
|
||||
continue
|
||||
fi
|
||||
|
||||
# Strip surrounding quotes (handles inline trailing comments like KEY="val" # note)
|
||||
if [[ "$_lth_value" == '"'* ]]; then
|
||||
_lth_value="${_lth_value#\"}"
|
||||
_lth_value="${_lth_value%%\"*}"
|
||||
elif [[ "$_lth_value" == "'"* ]]; then
|
||||
_lth_value="${_lth_value#\'}"
|
||||
_lth_value="${_lth_value%%\'*}"
|
||||
fi
|
||||
|
||||
if [[ "$_lth_value" =~ $_lth_ref_re ]]; then
|
||||
# Safe colour variable reference — resolve via indirection
|
||||
_lth_varname="${_lth_value#\$}"
|
||||
_lth_varname="${_lth_varname#\{}"
|
||||
_lth_varname="${_lth_varname%\}}"
|
||||
export "$_lth_key"="${!_lth_varname}"
|
||||
elif [[ "$_lth_value" =~ $_lth_ansi_re ]]; then
|
||||
# Raw ANSI escape literal — accept as-is
|
||||
export "$_lth_key"="$_lth_value"
|
||||
else
|
||||
printf "[ Warning ] load_theme: %s:%d: invalid value for '%s', ignoring.\n" \
|
||||
"$theme_file" "$_lth_lineno" "$_lth_key" >&2
|
||||
fi
|
||||
done < "$theme_file"
|
||||
}
|
||||
# Not exported, it remains private
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Dynamically switch the prompt theme for the current shell session.
|
||||
# Calls load_theme to apply the new colour values immediately, then updates
|
||||
# PROMPT_THEME so subshells and the set_prompt fallback chain reflect the
|
||||
# change. PROMPT_THEME_DIR is honoured when set.
|
||||
# Usage: set_theme [theme_name_or_path]
|
||||
# With no argument (or -l / --list), lists available .theme files.
|
||||
set_theme()
|
||||
{
|
||||
local theme_dir="${PROMPT_THEME_DIR:-${MYPATH}/profile.d/themes}"
|
||||
|
||||
# -- help mode -----------------------------------------------------------
|
||||
if [[ "$1" == "-h" || "$1" == "--help" ]]; then
|
||||
printf "set_theme: Switch the prompt colour theme for the current shell session.\n\n"
|
||||
printf "Usage: set_theme [options] [theme]\n\n"
|
||||
printf "Options:\n"
|
||||
printf " -h, --help Display this help screen\n"
|
||||
printf " -l, --list List available themes (default when no argument is given)\n\n"
|
||||
printf "Arguments:\n"
|
||||
printf " theme Bare theme name (e.g. 'dark') or an explicit path to a .theme file.\n"
|
||||
printf " Themes are searched in: %s\n" "$theme_dir"
|
||||
printf " Override with PROMPT_THEME_DIR in profile.conf [prompt].\n\n"
|
||||
printf "Examples:\n"
|
||||
printf " set_theme — list available themes\n"
|
||||
printf " set_theme dark — apply the dark theme\n"
|
||||
printf " set_theme ~/my.theme — apply a theme by path\n"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# -- list mode -----------------------------------------------------------
|
||||
if [[ $# -eq 0 || "$1" == "-l" || "$1" == "--list" ]]; then
|
||||
printf "Available themes in %s:\n" "$theme_dir"
|
||||
local f name
|
||||
for f in "$theme_dir"/*.theme; do
|
||||
[[ -f "$f" ]] || continue
|
||||
name="${f##*/}"
|
||||
name="${name%.theme}"
|
||||
if [[ "$name" == "${PROMPT_THEME:-}" ]]; then
|
||||
printf " * %s (active)\n" "$name"
|
||||
else
|
||||
printf " %s\n" "$name"
|
||||
fi
|
||||
done
|
||||
return 0
|
||||
fi
|
||||
|
||||
# -- apply mode ----------------------------------------------------------
|
||||
local theme_name="$1"
|
||||
|
||||
# Reset colours to defaults before loading the new theme
|
||||
set_colors
|
||||
load_theme "$theme_name" || return 1
|
||||
|
||||
export PROMPT_THEME="$theme_name"
|
||||
disp I "Prompt theme set to $theme_name."
|
||||
}
|
||||
export -f set_theme
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# timer_* functions : internal timing function for prompt
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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 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))
|
||||
local delta_us=$((($(timer_now) - timer_start) / 1000))
|
||||
local us=$((delta_us % 1000))
|
||||
local ms=$(((delta_us / 1000) % 1000))
|
||||
local s=$(((delta_us / 1000000) % 60))
|
||||
@@ -74,55 +278,84 @@ function timer_stop
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Function triguered internaly by bash : defining 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()
|
||||
{
|
||||
local Last_Command=$? # Must come first!
|
||||
local Blue='\[\e[0;34m\]'
|
||||
local White='\[\e[01;37m\]'
|
||||
local Yellow='\[\e[01;93m\]'
|
||||
local Red='\[\e[01;31m\]'
|
||||
local Green='\[\e[01;32m\]'
|
||||
local OnGrey='\[\e[47m\]'
|
||||
local OnRed='\[\e[41m\]'
|
||||
local OnBlue='\[\e[44m\]'
|
||||
local ICyan='\[\e[0;96m\]'
|
||||
local Default='\[\e[00m\]'
|
||||
local FancyX='\342\234\227'
|
||||
local Checkmark='\342\234\223'
|
||||
|
||||
# Begin with time
|
||||
PS1="\[\e[s$Blue$OnGrey [ \t ] $OnBlue"
|
||||
# Resolve theme/config colours with hardcoded fallbacks
|
||||
local _time_fg="${PROMPT_COLOR_TIME_FG:-$Blue}"
|
||||
local _time_bg="${PROMPT_COLOR_TIME_BG:-$On_White}"
|
||||
local _bar_bg="${PROMPT_COLOR_BAR_BG:-$On_Blue}"
|
||||
local _ok_fg="${PROMPT_COLOR_OK_FG:-$BWhite}"
|
||||
local _ok_mark="${PROMPT_COLOR_OK_MARK:-$BGreen}"
|
||||
local _err_bg="${PROMPT_COLOR_ERR_BG:-$On_Red}"
|
||||
local _err_fg="${PROMPT_COLOR_ERR_FG:-$White}"
|
||||
local _err_mark="${PROMPT_COLOR_ERR_MARK:-$BYellow}"
|
||||
local _root_fg="${PROMPT_COLOR_ROOT_FG:-$Red}"
|
||||
local _user_fg="${PROMPT_COLOR_USER_FG:-$BGreen}"
|
||||
local _dir_fg="${PROMPT_COLOR_DIR_FG:-$ICyan}"
|
||||
|
||||
# Add a bright white exit status for the last command
|
||||
# Begin with time (cursor-save is non-printing; all ANSI sequences wrapped
|
||||
# in \[...\] so bash does not count them toward the visible line width).
|
||||
# Every fg colour is combined with its section bg in the same \[...\] block
|
||||
# so that even "reset" colours (0;Xm) cannot strip the background.
|
||||
PS1="\[\e[s\]\[${_time_fg}${_time_bg}\] [ \t ] \[${_bar_bg}\]"
|
||||
|
||||
# If it was successful, print a green check mark. Otherwise, print
|
||||
# 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 "
|
||||
else
|
||||
PS1+="$White$OnRed [ \$Last_Command "
|
||||
PS1+="$Yellow$FancyX "
|
||||
fi
|
||||
|
||||
# Add the ellapsed time and current date
|
||||
PS1+="\[${_ok_fg}${_bar_bg}\] [ $Last_Command "
|
||||
PS1+="\[${_ok_mark}${_bar_bg}\]${Checkmark} "
|
||||
# Add the elapsed time, then close the status section and return to bar bg.
|
||||
timer_stop
|
||||
PS1+="($timer_show)$White ] $OnBlue "
|
||||
|
||||
# If root, just print the host in red. Otherwise, print the current user
|
||||
# and host in green.
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
PS1+="$Red\\u$Green@\\h"
|
||||
PS1+="($timer_show)\[${_ok_fg}${_bar_bg}\] ] "
|
||||
else
|
||||
PS1+="$Green\\u@\\h"
|
||||
PS1+="\[${_err_fg}${_err_bg}\] [ $Last_Command "
|
||||
PS1+="\[${_err_mark}${_err_bg}\]${FancyX} "
|
||||
timer_stop
|
||||
PS1+="($timer_show)\[${_err_fg}${_err_bg}\] ] "
|
||||
fi
|
||||
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 "
|
||||
|
||||
|
||||
# If root, print the host in root colour. Otherwise use user colour.
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
PS1+="\[${_root_fg}${_bar_bg}\] \\u\[${_user_fg}${_bar_bg}\]@\\h"
|
||||
else
|
||||
PS1+="\[${_user_fg}${_bar_bg}\] \\u@\\h"
|
||||
fi
|
||||
PS1+="\[\e[K\e[u\]\[$RESETCOL\]\n"
|
||||
# Print the working directory and prompt marker, then reset colour.
|
||||
PS1+="\[${_dir_fg}\]\\w \\\$\[$RESETCOL\] "
|
||||
}
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Theme and configuration loading.
|
||||
# Precedence (lowest → highest):
|
||||
# 1. Hardcoded fallbacks in set_prompt
|
||||
# 2. Theme file (PROMPT_THEME key from [prompt] section)
|
||||
# 3. Individual PROMPT_COLOR_* overrides in [prompt] section
|
||||
#
|
||||
# CONF_prompt is already populated by parse_conf (run in profile.sh before
|
||||
# modules are sourced). We extract PROMPT_THEME and PROMPT_THEME_DIR from the
|
||||
# raw associative array now so load_theme can run before load_conf "prompt"
|
||||
# exports remaining keys. That way any PROMPT_COLOR_* value set explicitly in
|
||||
# [prompt] wins over the same variable set by the theme file.
|
||||
_pt_theme="${CONF_prompt[PROMPT_THEME]:-}"
|
||||
_pt_dir="${CONF_prompt[PROMPT_THEME_DIR]:-}"
|
||||
[[ -n "$_pt_theme" ]] && load_theme "$_pt_theme" ${_pt_dir:+"$_pt_dir"}
|
||||
unset _pt_theme _pt_dir
|
||||
|
||||
load_conf "prompt"
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# EOF
|
||||
|
||||
523
profile.d/pwd.sh
523
profile.d/pwd.sh
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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,
|
||||
@@ -36,155 +36,448 @@
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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 extcar
|
||||
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 one or more 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
|
||||
local PARSED
|
||||
PARSED=$(getopt -o hsnule:L:o: --long \
|
||||
help,nosymbols,nonumbers,noup,nolow,extracars:,length:,occurences:,occurrences: \
|
||||
-n 'genpwd' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then return 1; fi
|
||||
eval set -- "$PARSED"
|
||||
|
||||
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")
|
||||
-s|--nosymbols)
|
||||
symb=0
|
||||
shift
|
||||
;;
|
||||
"-n" | "--nonumbers")
|
||||
-n|--nonumbers)
|
||||
numb=0
|
||||
shift
|
||||
;;
|
||||
"-u" | "--noup")
|
||||
-u|--noup)
|
||||
maj=0
|
||||
shift
|
||||
;;
|
||||
"-l" | "--nolow")
|
||||
-l|--nolow)
|
||||
min=0
|
||||
shift
|
||||
;;
|
||||
"-e"?* | "--extracars"?*)
|
||||
extcar=$(echo "$opt" | cut -f 2- -d '=')
|
||||
-e|--extracars)
|
||||
extcar="$2"
|
||||
shift 2
|
||||
;;
|
||||
"-L"?* | "--length"?*)
|
||||
local length=$(echo "$opt" | cut -f 2- -d '=')
|
||||
if ! [[ $length =~ ^[0-9]+$ ]]; then
|
||||
disp E "The --length parameter requires a number."
|
||||
-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"?*)
|
||||
local occurs=$(echo "$opt" | cut -f 2- -d '=')
|
||||
if ! [[ $occurs =~ ^[1-9]+$ ]]; then
|
||||
disp E "The --occurs parameter requires a number from 1 to 9."
|
||||
-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
|
||||
;;
|
||||
"-*")
|
||||
disp E "Unknow parameter ${opt}."
|
||||
return 1
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
if ! [[ $opt =~ ^[1-9]+$ ]]; then
|
||||
disp E "Unknow parameter ${opt}."
|
||||
disp E "Invalid options, use \"genpwd --help\" to display usage."
|
||||
return 1
|
||||
else
|
||||
nbpwd=$opt
|
||||
fi
|
||||
;;
|
||||
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="${1:RANDOM%${#1}:1} $RANDOM"
|
||||
if [[ $(awk -F"$char" '{print NF-1}' <<<"$picked") -gt $occurs ]]; then
|
||||
unset char
|
||||
fi
|
||||
done
|
||||
picked+="$char"
|
||||
echo "$char"
|
||||
}
|
||||
|
||||
disp I "Generating $nbpwd passwords, please wait..."
|
||||
for n in $(seq 1 $nbpwd); do
|
||||
{
|
||||
local carset='' # store final caracter set to use
|
||||
local picked='' # store already used caracter
|
||||
local rlength=0 # store already assigned length of caracters
|
||||
|
||||
# ?, *, $ and \ impossible to use to my knowledge as it would be interpreted
|
||||
if [[ $symb == 1 ]]; then
|
||||
pickcar '!.@#&%/^-_'
|
||||
carset+='!.@#&%/^-_'
|
||||
((rlength++))
|
||||
fi
|
||||
if [[ $numb == 1 ]]; then
|
||||
pickcar '0123456789'
|
||||
carset+='0123456789'
|
||||
((rlength++))
|
||||
fi
|
||||
if [[ $maj == 1 ]]; then
|
||||
pickcar 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
carset+='ABCDEFGHIJKLMNOPQRSTUVWXYZ'
|
||||
((rlength++))
|
||||
fi
|
||||
if [[ $min == 1 ]]; then
|
||||
pickcar 'abcdefghijklmnopqrstuvwxyz'
|
||||
carset+='abcdefghijklmnopqrstuvwxyz'
|
||||
((rlength++))
|
||||
fi
|
||||
if [[ -n $extcar ]]; then
|
||||
pickcar "$extcar"
|
||||
carset+=$extcar
|
||||
((rlength++))
|
||||
fi
|
||||
|
||||
# Check if we have enough car to have something viable
|
||||
if [[ ${#carset} -lt $length ]]; then
|
||||
disp E 'Not enought caracters are authorised for the password length.'
|
||||
disp E 'Please allow more caracter (preferably) or reduce password lentgh.'
|
||||
if [[ $# -gt 1 ]]; then
|
||||
disp E "Too many positional arguments. Use only [nb_passwd]."
|
||||
return 1
|
||||
fi
|
||||
|
||||
for i in $(seq 1 $(($length - $rlength))); do
|
||||
pickcar "$carset"
|
||||
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
|
||||
|
||||
if [[ -z $carset ]]; then
|
||||
disp E "No characters are available. Re-enable at least one character class."
|
||||
return 1
|
||||
fi
|
||||
|
||||
for (( i=0; i<${#carset}; i++ )); do
|
||||
ch=${carset:i:1}
|
||||
if [[ -z ${seen_chars["$ch"]+x} ]]; then
|
||||
seen_chars["$ch"]=1
|
||||
unique_carset+="$ch"
|
||||
fi
|
||||
done
|
||||
} | sort -R | awk '{printf "%s", $1}'
|
||||
unset picked carset rlength
|
||||
echo
|
||||
unset seen_chars
|
||||
carset="$unique_carset"
|
||||
|
||||
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,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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,
|
||||
@@ -35,166 +35,221 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Let the rain fall
|
||||
# ------------------------------------------------------------------------------
|
||||
rain()
|
||||
# Generic rain-like engine and presets
|
||||
|
||||
_rain_build_colors()
|
||||
{
|
||||
show_usage() {
|
||||
echo -e "Usage: rain [OPTIONS]"
|
||||
echo -e ""
|
||||
echo -e "Options:"
|
||||
echo -e " -s, --speed NUM Set the drop delay in seconds (default: 0.050)."
|
||||
echo -e " Lower values = faster rain."
|
||||
echo -e " -c, --color COLOR Set the color theme (default: white)."
|
||||
echo -e " -h, --help Display this help message and exit."
|
||||
echo -e ""
|
||||
echo -e "Available Colors:"
|
||||
echo -e " \e[32mgreen\e[0m : The classic Matrix digital rain"
|
||||
echo -e " \e[34mblue\e[0m : Deep ocean blue gradients"
|
||||
echo -e " \e[31mred\e[0m : Crimson/Blood rain"
|
||||
echo -e " \e[33myellow\e[0m : Amber and gold tones"
|
||||
echo -e " \e[36mcyan\e[0m : Electric cyan/turquoise"
|
||||
echo -e " white : Greyscale and white (original style)"
|
||||
echo -e ""
|
||||
echo -e "Example: rain --color green --speed 0.03"
|
||||
}
|
||||
local base_color="$1"
|
||||
RAIN_ENGINE_COLORS=()
|
||||
|
||||
local step_duration=0.050
|
||||
local base_color="white" # default color scheme, can be overridden by --color
|
||||
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
|
||||
}
|
||||
|
||||
# Analyse arguments
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
-s|--speed)
|
||||
if [[ -n "$2" && ! "$2" =~ ^- ]]; then
|
||||
step_duration="$2"; shift
|
||||
else
|
||||
echo -e "\e[31mError: --speed requires a numeric value.\e[0m"
|
||||
show_usage && return 1
|
||||
fi
|
||||
_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")
|
||||
;;
|
||||
-c|--color)
|
||||
if [[ -n "$2" && ! "$2" =~ ^- ]]; then
|
||||
base_color="$2"; shift
|
||||
else
|
||||
echo -e "\e[31mError: --color requires a color name.\e[0m"
|
||||
show_usage && return 1
|
||||
fi
|
||||
kana|kanji)
|
||||
# Half-width katakana set, generally rendered as single-cell glyphs.
|
||||
RAIN_ENGINE_CHARS=("ア" "イ" "ウ" "エ" "オ" "カ" "キ" "ク" "ケ" "コ" "サ" "シ" "ス" "セ" "ソ" "タ" "チ" "ツ" "テ" "ト" "ナ" "ニ" "ヌ" "ネ" "ノ" "ハ" "ヒ" "フ" "ヘ" "ホ" "マ" "ミ" "ム" "メ" "モ" "ヤ" "ユ" "ヨ" "ラ" "リ" "ル" "レ" "ロ" "ワ" "ン")
|
||||
;;
|
||||
-h|--help)
|
||||
show_usage && return 0
|
||||
ascii)
|
||||
RAIN_ENGINE_CHARS=("0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "A" "B" "C" "D" "E" "F")
|
||||
;;
|
||||
*)
|
||||
echo -e "\e[31mUnknown option: $1\e[0m"
|
||||
show_usage && return 1
|
||||
disp E "Unknown charset: ${charset} (supported: binary, kana, ascii)."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# Define colors (256-colors gradients)
|
||||
local rain_colors=()
|
||||
case $base_color in
|
||||
green) # Matrix style green
|
||||
for i in {22..28} {34..40} {46..48}; do rain_colors+=("\e[38;5;${i}m"); done ;;
|
||||
blue) # Deep ocean blues
|
||||
for i in {17..21} {27..33} {39..45}; do rain_colors+=("\e[38;5;${i}m"); done ;;
|
||||
red) # Crimson / blood red
|
||||
for i in {52..52} {88..88} {124..124} {160..160} {196..201}; do rain_colors+=("\e[38;5;${i}m"); done ;;
|
||||
yellow) # Amber / gold
|
||||
for i in {58..58} {100..100} {142..142} {184..184} {226..229}; do rain_colors+=("\e[38;5;${i}m"); done ;;
|
||||
cyan) # Electric cyan / turquoise
|
||||
for i in {30..31} {37..38} {44..45} {50..51}; do rain_colors+=("\e[38;5;${i}m"); done ;;
|
||||
*) # Greyscale / white (original style)
|
||||
rain_colors=("\e[37m" "\e[37;1m")
|
||||
for i in {244..255}; do rain_colors+=("\e[38;5;${i}m"); done ;;
|
||||
;;
|
||||
*)
|
||||
RAIN_ENGINE_CHARS=("|" "│" "┃" "┆" "┇" "┊" "┋" "╽" "╿")
|
||||
;;
|
||||
esac
|
||||
|
||||
local exit_st=0
|
||||
local rain_cars=("|" "│" "┃" "┆" "┇" "┊" "┋" "╽" "╿")
|
||||
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 X=0 Y=0 drop_length=0 rain_drop=0
|
||||
local max_rain_width=0 new_rain_odd=0 falling_odd=0
|
||||
return 0
|
||||
}
|
||||
|
||||
sigwinch() {
|
||||
_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
|
||||
}
|
||||
|
||||
_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)
|
||||
#step_duration=0.025
|
||||
((max_rain_width = term_width * term_height / 4))
|
||||
((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))
|
||||
# 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))
|
||||
frame_sleep="$step_duration"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
do_exit() {
|
||||
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 "
|
||||
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}")
|
||||
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}"
|
||||
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
|
||||
read -r -n 1 -t "$frame_sleep" ch
|
||||
case "$ch" in
|
||||
q | Q)
|
||||
q|Q)
|
||||
do_exit
|
||||
;;
|
||||
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]}"
|
||||
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))
|
||||
@@ -203,20 +258,183 @@ rain()
|
||||
((num_rains++))
|
||||
fi
|
||||
|
||||
# Let rain fall!
|
||||
do_render
|
||||
fi
|
||||
done
|
||||
echo -ne "\e[${term_height};1H\e[0K"
|
||||
|
||||
# Show cursor and echo stdin
|
||||
echo -ne "\e[?25h"
|
||||
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
|
||||
|
||||
211
profile.d/ssh.sh
211
profile.d/ssh.sh
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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,
|
||||
@@ -35,82 +35,173 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Remove host from know_host (name and IP) for the active user
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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
|
||||
disp E "Incorrect number of parameters."
|
||||
disp E "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' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
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
|
||||
if ! ip=$(host "$hst" 2>/dev/null | awk '/has address/ {print $NF; exit}'); then
|
||||
disp E "Impossible to extract IP from hostname." &&
|
||||
return 1
|
||||
fi
|
||||
[[ -z $ip ]] && {
|
||||
disp E "Impossible to extract IP from hostname."
|
||||
return 1;
|
||||
}
|
||||
fi
|
||||
|
||||
if [[ $hst ]]; then
|
||||
disp I "Removing host $hst from ssh known_host..."
|
||||
ssh-keygen -R $hst >/dev/null
|
||||
fi
|
||||
if [[ $ip ]]; then
|
||||
disp I "Removing IP $ip from ssh known_host..."
|
||||
ssh-keygen -R $ip >/dev/null
|
||||
fi
|
||||
unset hst ip
|
||||
done
|
||||
}
|
||||
export -f rmhost
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Login root via SSH on the given machine
|
||||
# ------------------------------------------------------------------------------
|
||||
ssr()
|
||||
{
|
||||
for opt in $@; do
|
||||
case $opt in
|
||||
"-h" | "--help")
|
||||
echo "ssr: do a root user ssh login."
|
||||
echo
|
||||
echo "Usage: ssr <server [ssh options]>"
|
||||
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
|
||||
|
||||
[[ $# -eq 0 ]] && {
|
||||
disp E "Missing argument. Use 'rmhost --help' for usage."
|
||||
return 1
|
||||
}
|
||||
|
||||
command -v ssh-keygen >/dev/null 2>&1 || {
|
||||
disp E "ssh-keygen is not installed."
|
||||
return 127
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
[[ ! $1 ]] &&
|
||||
disp E "Please specify the server you want to log in." &&
|
||||
|
||||
[[ $# -eq 0 || -z ${1:-} ]] && {
|
||||
disp E "Please specify the server you want to log in."
|
||||
return 1
|
||||
}
|
||||
|
||||
local srv=$1 && shift
|
||||
local srv=$1
|
||||
shift
|
||||
|
||||
ssh -Y root@"$srv" "$@"
|
||||
# Build default options array from config, falling back to -Y (X11 forwarding)
|
||||
local -a ssh_default_opts=()
|
||||
if [[ -n "${SSH_DEFAULT_OPT:-}" ]]; then
|
||||
read -ra ssh_default_opts <<< "${SSH_DEFAULT_OPT}"
|
||||
else
|
||||
ssh_default_opts=(-Y)
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC2029
|
||||
ssh "${ssh_default_opts[@]}" root@"$srv" "$@"
|
||||
}
|
||||
export -f ssr
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
load_conf "ssh"
|
||||
|
||||
# EOF
|
||||
|
||||
42
profile.d/themes/abyss.theme
Normal file
42
profile.d/themes/abyss.theme
Normal file
@@ -0,0 +1,42 @@
|
||||
# Abyss prompt theme — deep ocean navy, electric teal, golden accents
|
||||
# ------------------------------------------------------------------------------
|
||||
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||
# line by line. Only the following value forms are accepted:
|
||||
#
|
||||
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||
# KEY="${ColorVarName}" — same with braces
|
||||
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||
#
|
||||
# Accepted keys:
|
||||
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||
# from disp.sh for the whole shell session)
|
||||
#
|
||||
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||
# warning — theme files cannot execute code.
|
||||
# ------------------------------------------------------------------------------
|
||||
# Inspired by the VS Code "Abyss" theme: deep navy/black backdrop, electric
|
||||
# teal highlights, golden-yellow accents, cool electric blue for identifiers.
|
||||
# Palette overrides shift the cooler hues to their high-intensity (electric)
|
||||
# equivalents — blue becomes IBlue, green shifts toward teal (ICyan).
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Palette overrides — shift colours toward cool electric hues
|
||||
Blue="\e[0;94m" # electric blue (IBlue — abyss identifier colour)
|
||||
Green="\e[0;96m" # teal (ICyan — abyss string colour)
|
||||
Yellow="\e[0;93m" # bright gold (IYellow — abyss constant colour)
|
||||
|
||||
PROMPT_COLOR_TIME_FG="$ICyan" # electric teal clock
|
||||
PROMPT_COLOR_TIME_BG="\e[48;2;0;60;70m" # very dark cyan bg for time
|
||||
PROMPT_COLOR_BAR_BG="\e[48;2;0;30;70m" # deep navy bar (24-bit)
|
||||
|
||||
PROMPT_COLOR_OK_FG="$ICyan" # teal on success
|
||||
PROMPT_COLOR_OK_MARK="$IGreen" # bright teal-green checkmark
|
||||
|
||||
PROMPT_COLOR_ERR_BG="\e[48;2;180;20;20m" # vivid crimson background (24-bit)
|
||||
PROMPT_COLOR_ERR_FG="\e[1;97m" # bold bright white — maximum contrast
|
||||
PROMPT_COLOR_ERR_MARK="$IYellow" # golden X
|
||||
|
||||
PROMPT_COLOR_ROOT_FG="$IRed" # red for root
|
||||
PROMPT_COLOR_USER_FG="$IBlue" # electric blue for user
|
||||
PROMPT_COLOR_DIR_FG="$ICyan" # teal path
|
||||
42
profile.d/themes/adwaita.theme
Normal file
42
profile.d/themes/adwaita.theme
Normal file
@@ -0,0 +1,42 @@
|
||||
# Adwaita prompt theme — clean GNOME defaults, blue accent, green/red status
|
||||
# ------------------------------------------------------------------------------
|
||||
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||
# line by line. Only the following value forms are accepted:
|
||||
#
|
||||
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||
# KEY="${ColorVarName}" — same with braces
|
||||
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||
#
|
||||
# Accepted keys:
|
||||
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||
# from disp.sh for the whole shell session)
|
||||
#
|
||||
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||
# warning — theme files cannot execute code.
|
||||
# ------------------------------------------------------------------------------
|
||||
# Follows the GNOME HIG colour intent: a single calm blue accent (#3584e4),
|
||||
# pleasant green for success (#33d17a), clear red for errors (#e01b24).
|
||||
# No intense saturation, no heavy remapping — legibility over spectacle.
|
||||
# Palette overrides gently shift Blue and Green to their IBlue/IGreen
|
||||
# variants to better match Adwaita's brighter, flatter tones.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Palette overrides — align to Adwaita's brighter accent tones
|
||||
Blue="\e[0;94m" # IBlue → closer to Adwaita blue (#3584e4)
|
||||
Green="\e[0;92m" # IGreen → closer to Adwaita green (#33d17a)
|
||||
|
||||
PROMPT_COLOR_TIME_FG="$Cyan" # calm cyan clock text
|
||||
PROMPT_COLOR_TIME_BG="$On_IBlack" # dark grey clock background
|
||||
PROMPT_COLOR_BAR_BG="$On_Blue" # blue main bar (Adwaita accent)
|
||||
|
||||
PROMPT_COLOR_OK_FG="$White" # clean white on success
|
||||
PROMPT_COLOR_OK_MARK="$Green" # Adwaita green checkmark
|
||||
|
||||
PROMPT_COLOR_ERR_BG="$On_Red" # Adwaita red on failure
|
||||
PROMPT_COLOR_ERR_FG="$BIWhite" # bold bright white for maximum legibility
|
||||
PROMPT_COLOR_ERR_MARK="$Yellow" # yellow X (warning intent)
|
||||
|
||||
PROMPT_COLOR_ROOT_FG="$Red" # Adwaita red for root
|
||||
PROMPT_COLOR_USER_FG="$BBlue" # darker bold blue — readable on blue bar
|
||||
PROMPT_COLOR_DIR_FG="$IGreen" # Adwaita green for path
|
||||
32
profile.d/themes/dark.theme
Normal file
32
profile.d/themes/dark.theme
Normal file
@@ -0,0 +1,32 @@
|
||||
# Dark prompt theme — dark grey bar, cyan user, magenta path
|
||||
# ------------------------------------------------------------------------------
|
||||
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||
# line by line. Only the following value forms are accepted:
|
||||
#
|
||||
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||
# KEY="${ColorVarName}" — same with braces
|
||||
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||
#
|
||||
# Accepted keys:
|
||||
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||
# from disp.sh for the whole shell session)
|
||||
#
|
||||
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||
# warning — theme files cannot execute code.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
PROMPT_COLOR_TIME_FG="$ICyan" # Clock text
|
||||
PROMPT_COLOR_TIME_BG="$On_IBlack" # Clock background (black)
|
||||
PROMPT_COLOR_BAR_BG="$On_Black" # Main bar background (dark grey)
|
||||
|
||||
PROMPT_COLOR_OK_FG="$IGreen" # Exit-code text on success
|
||||
PROMPT_COLOR_OK_MARK="$BGreen" # Checkmark colour on success
|
||||
|
||||
PROMPT_COLOR_ERR_BG="$On_Red" # Exit-code background on failure
|
||||
PROMPT_COLOR_ERR_FG="$BIWhite" # Exit-code text on failure
|
||||
PROMPT_COLOR_ERR_MARK="$BIYellow" # X mark colour on failure
|
||||
|
||||
PROMPT_COLOR_ROOT_FG="$BIRed" # Username colour when root
|
||||
PROMPT_COLOR_USER_FG="$ICyan" # Username@host colour for normal users
|
||||
PROMPT_COLOR_DIR_FG="$IPurple" # Working directory colour
|
||||
32
profile.d/themes/default.theme
Normal file
32
profile.d/themes/default.theme
Normal file
@@ -0,0 +1,32 @@
|
||||
# Default prompt theme — blue bar, green user, cyan path
|
||||
# ------------------------------------------------------------------------------
|
||||
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||
# line by line. Only the following value forms are accepted:
|
||||
#
|
||||
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||
# KEY="${ColorVarName}" — same with braces
|
||||
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||
#
|
||||
# Accepted keys:
|
||||
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||
# from disp.sh for the whole shell session)
|
||||
#
|
||||
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||
# warning — theme files cannot execute code.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
PROMPT_COLOR_TIME_FG="$Blue" # Clock text
|
||||
PROMPT_COLOR_TIME_BG="$On_White" # Clock background (dark grey)
|
||||
PROMPT_COLOR_BAR_BG="$On_Blue" # Main bar background
|
||||
|
||||
PROMPT_COLOR_OK_FG="$White" # Exit-code text on success
|
||||
PROMPT_COLOR_OK_MARK="$Green" # Checkmark colour on success
|
||||
|
||||
PROMPT_COLOR_ERR_BG="$On_Red" # Exit-code background on failure
|
||||
PROMPT_COLOR_ERR_FG="$White" # Exit-code text on failure
|
||||
PROMPT_COLOR_ERR_MARK="$BYellow" # X mark colour on failure
|
||||
|
||||
PROMPT_COLOR_ROOT_FG="$Red" # Username colour when root
|
||||
PROMPT_COLOR_USER_FG="$Green" # Username@host colour for normal users
|
||||
PROMPT_COLOR_DIR_FG="$ICyan" # Working directory colour
|
||||
35
profile.d/themes/light.theme
Normal file
35
profile.d/themes/light.theme
Normal file
@@ -0,0 +1,35 @@
|
||||
# Light prompt theme — white bar, blue user, purple path
|
||||
# ------------------------------------------------------------------------------
|
||||
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||
# line by line. Only the following value forms are accepted:
|
||||
#
|
||||
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||
# KEY="${ColorVarName}" — same with braces
|
||||
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||
#
|
||||
# Accepted keys:
|
||||
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||
# from disp.sh for the whole shell session)
|
||||
#
|
||||
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||
# warning — theme files cannot execute code.
|
||||
# ------------------------------------------------------------------------------
|
||||
# Opposite of dark.theme: backgrounds flip to bright whites, foregrounds
|
||||
# shift to their dark/regular equivalents for contrast on a light terminal.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
PROMPT_COLOR_TIME_FG="$BBlack" # Clock text (bold black — forces true black on terminals that render Black as dark grey)
|
||||
PROMPT_COLOR_TIME_BG="$On_IWhite" # Clock background (On_Black → On_IWhite)
|
||||
PROMPT_COLOR_BAR_BG="$On_White" # Main bar background (On_IBlack → On_White)
|
||||
|
||||
PROMPT_COLOR_OK_FG="$Green" # Exit-code text on success (IGreen → Green)
|
||||
PROMPT_COLOR_OK_MARK="$BGreen" # Checkmark colour on success (unchanged — bold green reads on both)
|
||||
|
||||
PROMPT_COLOR_ERR_BG="$On_Red" # Exit-code background on failure (unchanged)
|
||||
PROMPT_COLOR_ERR_FG="$BIWhite" # Exit-code text on failure (unchanged — white on red works on both)
|
||||
PROMPT_COLOR_ERR_MARK="$BYellow" # X mark on failure (BIYellow → BYellow, less glaring on light)
|
||||
|
||||
PROMPT_COLOR_ROOT_FG="$Red" # Username when root (BIRed → Red)
|
||||
PROMPT_COLOR_USER_FG="$Blue" # Username@host normal user (ICyan → Blue)
|
||||
PROMPT_COLOR_DIR_FG="$Purple" # Working directory (IPurple → Purple)
|
||||
64
profile.d/themes/monochrome.theme
Normal file
64
profile.d/themes/monochrome.theme
Normal file
@@ -0,0 +1,64 @@
|
||||
# Monochrome prompt theme — strict greyscale, no hue at all
|
||||
# ------------------------------------------------------------------------------
|
||||
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||
# line by line. Only the following value forms are accepted:
|
||||
#
|
||||
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||
# KEY="${ColorVarName}" — same with braces
|
||||
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||
#
|
||||
# Accepted keys:
|
||||
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||
# from disp.sh for the whole shell session)
|
||||
#
|
||||
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||
# warning — theme files cannot execute code.
|
||||
# ------------------------------------------------------------------------------
|
||||
# All hues are silenced — colour variables are remapped to greyscale ANSI
|
||||
# codes so that ls, man, grep, etc. also lose their colour cues. Contrast
|
||||
# is achieved entirely through brightness: dark grey / light grey / white.
|
||||
# Error state uses an inverted (white background, black text) bar so it
|
||||
# remains visually distinct without relying on red.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Palette overrides — replace every hue with a grey equivalent
|
||||
Red="\e[0;37m" # light grey
|
||||
Green="\e[0;97m" # bright white (success intent kept as brightest)
|
||||
Yellow="\e[0;90m" # dark grey
|
||||
Blue="\e[0;90m" # dark grey
|
||||
Purple="\e[0;37m" # light grey
|
||||
Cyan="\e[0;37m" # light grey
|
||||
BRed="\e[1;37m" # bold light grey
|
||||
BGreen="\e[1;97m" # bold bright white
|
||||
BYellow="\e[1;90m" # bold dark grey
|
||||
BBlue="\e[1;90m" # bold dark grey
|
||||
BPurple="\e[1;37m" # bold light grey
|
||||
BCyan="\e[1;37m" # bold light grey
|
||||
IRed="\e[0;97m" # bright white
|
||||
IGreen="\e[0;97m" # bright white
|
||||
IYellow="\e[0;90m" # dark grey
|
||||
IBlue="\e[0;90m" # dark grey
|
||||
IPurple="\e[0;37m" # light grey
|
||||
ICyan="\e[0;37m" # light grey
|
||||
BIRed="\e[1;97m" # bold bright white
|
||||
BIGreen="\e[1;97m" # bold bright white
|
||||
BIYellow="\e[1;90m" # bold dark grey
|
||||
BIBlue="\e[1;90m" # bold dark grey
|
||||
BIPurple="\e[1;37m" # bold light grey
|
||||
BICyan="\e[1;37m" # bold light grey
|
||||
|
||||
PROMPT_COLOR_TIME_FG="$IBlack" # dark grey clock text (subtle)
|
||||
PROMPT_COLOR_TIME_BG="$On_IBlack" # dark grey clock background
|
||||
PROMPT_COLOR_BAR_BG="$On_IBlack" # dark grey main bar
|
||||
|
||||
PROMPT_COLOR_OK_FG="$IWhite" # bright white on success
|
||||
PROMPT_COLOR_OK_MARK="$BIWhite" # bold bright white checkmark
|
||||
|
||||
PROMPT_COLOR_ERR_BG="$On_White" # inverted: bright white bar on error
|
||||
PROMPT_COLOR_ERR_FG="$Black" # black text on white background
|
||||
PROMPT_COLOR_ERR_MARK="$BBlack" # bold black X
|
||||
|
||||
PROMPT_COLOR_ROOT_FG="$BIWhite" # bold bright white for root warning
|
||||
PROMPT_COLOR_USER_FG="$IWhite" # bright white for normal user
|
||||
PROMPT_COLOR_DIR_FG="$White" # standard white for path
|
||||
45
profile.d/themes/monokai.theme
Normal file
45
profile.d/themes/monokai.theme
Normal file
@@ -0,0 +1,45 @@
|
||||
# Monokai prompt theme — high-saturation, vivid hues on near-black
|
||||
# ------------------------------------------------------------------------------
|
||||
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||
# line by line. Only the following value forms are accepted:
|
||||
#
|
||||
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||
# KEY="${ColorVarName}" — same with braces
|
||||
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||
#
|
||||
# Accepted keys:
|
||||
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||
# from disp.sh for the whole shell session)
|
||||
#
|
||||
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||
# warning — theme files cannot execute code.
|
||||
# ------------------------------------------------------------------------------
|
||||
# Monokai's signature: lime green, orange-yellow, hot pink/red, bright violet,
|
||||
# electric cyan — all on a near-black (#272822) background.
|
||||
# Palette overrides remap the dim ANSI regulars to their vivid high-intensity
|
||||
# equivalents so that ls, grep colour output, etc. also look more "Monokai".
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Palette overrides — boost regular colours to Monokai-vivid equivalents
|
||||
Red="\e[0;91m" # hot pink/red (#F92672)
|
||||
Green="\e[0;92m" # lime green (#A6E22E)
|
||||
Yellow="\e[0;93m" # orange-yellow (#E6DB74 / #FD971F)
|
||||
Blue="\e[0;94m" # electric blue (#66D9E8 → shift blue)
|
||||
Purple="\e[0;95m" # bright violet (#AE81FF)
|
||||
Cyan="\e[0;96m" # electric cyan (#66D9E8)
|
||||
|
||||
PROMPT_COLOR_TIME_FG="$IYellow" # orange clock text
|
||||
PROMPT_COLOR_TIME_BG="$On_IBlack" # near-black Monokai background
|
||||
PROMPT_COLOR_BAR_BG="$On_IBlack" # flat dark bar
|
||||
|
||||
PROMPT_COLOR_OK_FG="$IGreen" # lime green on success
|
||||
PROMPT_COLOR_OK_MARK="$IGreen" # lime green checkmark
|
||||
|
||||
PROMPT_COLOR_ERR_BG="$On_IBlack" # keep dark — use colour for contrast
|
||||
PROMPT_COLOR_ERR_FG="$IRed" # hot pink on failure
|
||||
PROMPT_COLOR_ERR_MARK="$IRed" # hot pink X
|
||||
|
||||
PROMPT_COLOR_ROOT_FG="$IRed" # hot pink for root
|
||||
PROMPT_COLOR_USER_FG="$IYellow" # orange-yellow for user
|
||||
PROMPT_COLOR_DIR_FG="$ICyan" # electric cyan for path
|
||||
42
profile.d/themes/plasma.theme
Normal file
42
profile.d/themes/plasma.theme
Normal file
@@ -0,0 +1,42 @@
|
||||
# Plasma prompt theme — vivid KDE Plasma purples and electric cyan
|
||||
# ------------------------------------------------------------------------------
|
||||
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||
# line by line. Only the following value forms are accepted:
|
||||
#
|
||||
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||
# KEY="${ColorVarName}" — same with braces
|
||||
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||
#
|
||||
# Accepted keys:
|
||||
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||
# from disp.sh for the whole shell session)
|
||||
#
|
||||
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||
# warning — theme files cannot execute code.
|
||||
# ------------------------------------------------------------------------------
|
||||
# Captures the charged, vivid energy of KDE Plasma: dominant bright magenta/
|
||||
# purple, electric cyan highlights, all on a dark background. Palette overrides
|
||||
# boost Blue, Purple, and Cyan to their high-intensity equivalents so that
|
||||
# shell colour output reflects the same vivid palette.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Palette overrides — vivid charged hues
|
||||
Blue="\e[0;94m" # electric blue (IBlue)
|
||||
Purple="\e[0;95m" # vivid magenta (IPurple — Plasma's signature colour)
|
||||
Cyan="\e[0;96m" # electric cyan (ICyan)
|
||||
|
||||
PROMPT_COLOR_TIME_FG="$BIPurple" # vivid purple clock text
|
||||
PROMPT_COLOR_TIME_BG="\e[48;2;50;50;55m" # deep charcoal (darker than On_IBlack)
|
||||
PROMPT_COLOR_BAR_BG="\e[48;2;75;0;130m" # deep indigo-purple (24-bit) — darker than On_Purple
|
||||
|
||||
PROMPT_COLOR_OK_FG="$ICyan" # electric cyan on success
|
||||
PROMPT_COLOR_OK_MARK="$IGreen" # green checkmark
|
||||
|
||||
PROMPT_COLOR_ERR_BG="$On_Red" # red bar on failure
|
||||
PROMPT_COLOR_ERR_FG="$IWhite" # bright white text
|
||||
PROMPT_COLOR_ERR_MARK="$IYellow" # yellow X
|
||||
|
||||
PROMPT_COLOR_ROOT_FG="$IRed" # red for root
|
||||
PROMPT_COLOR_USER_FG="$BIPurple" # bold vivid purple for user
|
||||
PROMPT_COLOR_DIR_FG="$ICyan" # electric cyan path
|
||||
127
profile.d/themes/solarized-light.theme
Normal file
127
profile.d/themes/solarized-light.theme
Normal file
@@ -0,0 +1,127 @@
|
||||
# Solarized Light prompt theme — exact 24-bit / true-colour palette
|
||||
# ------------------------------------------------------------------------------
|
||||
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||
# line by line. Only the following value forms are accepted:
|
||||
#
|
||||
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||
# KEY="${ColorVarName}" — same with braces
|
||||
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||
#
|
||||
# Accepted keys:
|
||||
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||
# from disp.sh for the whole shell session)
|
||||
#
|
||||
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||
# warning — theme files cannot execute code.
|
||||
# ------------------------------------------------------------------------------
|
||||
# Requires a terminal with true-colour / 24-bit support.
|
||||
# Check with: printf '\e[38;2;220;50;47mred\e[0m\n'
|
||||
# If you see solid red text, your terminal supports this theme.
|
||||
# (konsole, iTerm2, kitty, alacritty, Windows Terminal all do)
|
||||
#
|
||||
# Solarized Light base tones (inverted vs Dark):
|
||||
# Base3 #fdf6e3 →253 246 227 (main background — lightest)
|
||||
# Base2 #eee8d5 →238 232 213 (background highlights)
|
||||
# Base1 #93a1a1 →147 161 161 (comments / secondary content)
|
||||
# Base0 #839496 →131 148 150 (body text — secondary)
|
||||
# Base00 #657b83 →101 123 131 (body text — main on light bg)
|
||||
# Base01 #586e75 → 88 110 117 (emphasis)
|
||||
# Base02 #073642 → 7 54 66 (darkest — used for strong contrast)
|
||||
#
|
||||
# Accent colours are identical to Solarized Dark:
|
||||
# Yellow #b58900 →181 137 0
|
||||
# Orange #cb4b16 →203 75 22
|
||||
# Red #dc322f →220 50 47
|
||||
# Magenta #d33682 →211 54 130
|
||||
# Violet #6c71c4 →108 113 196
|
||||
# Blue #268bd2 → 38 139 210
|
||||
# Cyan #2aa198 → 42 161 152
|
||||
# Green #859900 →133 153 0
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# ---- Foreground palette overrides -------------------------------------------
|
||||
# The accent fg colours are identical to Dark — only the base/neutral roles flip.
|
||||
# Regular
|
||||
Black="\e[38;2;253;246;227m" # Base3 — lightest (fg on light bg = invisible; used as bg fg pair complement)
|
||||
Red="\e[38;2;220;50;47m" # Red
|
||||
Green="\e[38;2;133;153;0m" # Green
|
||||
Yellow="\e[38;2;181;137;0m" # Yellow — primary accent
|
||||
Blue="\e[38;2;38;139;210m" # Blue
|
||||
Purple="\e[38;2;211;54;130m" # Magenta
|
||||
Cyan="\e[38;2;42;161;152m" # Cyan
|
||||
White="\e[38;2;101;123;131m" # Base00 — main body text on light bg
|
||||
|
||||
# Bold
|
||||
BBlack="\e[1;38;2;238;232;213m" # Base2 bold
|
||||
BRed="\e[1;38;2;220;50;47m" # Red bold
|
||||
BGreen="\e[1;38;2;133;153;0m" # Green bold
|
||||
BYellow="\e[1;38;2;181;137;0m" # Yellow bold
|
||||
BBlue="\e[1;38;2;38;139;210m" # Blue bold
|
||||
BPurple="\e[1;38;2;211;54;130m" # Magenta bold
|
||||
BCyan="\e[1;38;2;42;161;152m" # Cyan bold
|
||||
BWhite="\e[1;38;2;88;110;117m" # Base01 bold — emphasis text
|
||||
|
||||
# High intensity (emphasis / I* family)
|
||||
IBlack="\e[38;2;147;161;161m" # Base1 — secondary/comments
|
||||
IRed="\e[38;2;203;75;22m" # Orange — Solarized's "bright red"
|
||||
IGreen="\e[38;2;133;153;0m" # Green (no brighter variant)
|
||||
IYellow="\e[38;2;181;137;0m" # Yellow (no brighter variant)
|
||||
IBlue="\e[38;2;108;113;196m" # Violet — Solarized's "bright blue"
|
||||
IPurple="\e[38;2;211;54;130m" # Magenta (no brighter variant)
|
||||
ICyan="\e[38;2;42;161;152m" # Cyan (no brighter variant)
|
||||
IWhite="\e[38;2;88;110;117m" # Base01 — emphasis
|
||||
|
||||
# Bold high intensity
|
||||
BIBlack="\e[1;38;2;147;161;161m" # Base1 bold
|
||||
BIRed="\e[1;38;2;203;75;22m" # Orange bold
|
||||
BIGreen="\e[1;38;2;133;153;0m" # Green bold
|
||||
BIYellow="\e[1;38;2;181;137;0m" # Yellow bold
|
||||
BIBlue="\e[1;38;2;108;113;196m" # Violet bold
|
||||
BIPurple="\e[1;38;2;211;54;130m" # Magenta bold
|
||||
BICyan="\e[1;38;2;42;161;152m" # Cyan bold
|
||||
BIWhite="\e[1;38;2;88;110;117m" # Base01 bold
|
||||
|
||||
# ---- Background palette overrides -------------------------------------------
|
||||
# Light bases flip: On_Black → Base3 (lightest), On_IBlack → Base2 (highlights)
|
||||
On_Black="\e[48;2;253;246;227m" # Base3 — main light background
|
||||
On_Red="\e[48;2;220;50;47m" # Red
|
||||
On_Green="\e[48;2;133;153;0m" # Green
|
||||
On_Yellow="\e[48;2;181;137;0m" # Yellow
|
||||
On_Blue="\e[48;2;38;139;210m" # Blue
|
||||
On_Purple="\e[48;2;211;54;130m" # Magenta
|
||||
On_Cyan="\e[48;2;42;161;152m" # Cyan
|
||||
On_White="\e[48;2;101;123;131m" # Base00
|
||||
|
||||
On_IBlack="\e[48;2;238;232;213m" # Base2 — background highlights (slightly darker than Base3)
|
||||
On_IRed="\e[48;2;203;75;22m" # Orange
|
||||
On_IGreen="\e[48;2;133;153;0m" # Green
|
||||
On_IYellow="\e[48;2;181;137;0m" # Yellow
|
||||
On_IBlue="\e[48;2;108;113;196m" # Violet
|
||||
On_IPurple="\e[48;2;211;54;130m" # Magenta
|
||||
On_ICyan="\e[48;2;42;161;152m" # Cyan
|
||||
On_IWhite="\e[48;2;88;110;117m" # Base01
|
||||
|
||||
# ---- Special codes ----------------------------------------------------------
|
||||
DEFAULTFG="\e[38;2;101;123;131m" # Base00 — default foreground on light bg
|
||||
DEFAULTBG="\e[48;2;253;246;227m" # Base3 — default background
|
||||
RESETCOL="\e[0m"
|
||||
|
||||
# ---- Prompt colour slots ----------------------------------------------------
|
||||
# All specified as direct ANSI sequences to avoid ordering dependencies
|
||||
# with the palette overrides above.
|
||||
|
||||
PROMPT_COLOR_TIME_FG="\e[38;2;181;137;0m" # Yellow — primary accent
|
||||
PROMPT_COLOR_TIME_BG="\e[48;2;238;232;213m" # Base2 — slightly darker than bg
|
||||
PROMPT_COLOR_BAR_BG="\e[48;2;238;232;213m" # Base2 — warm light bar
|
||||
|
||||
PROMPT_COLOR_OK_FG="\e[38;2;7;54;66m" # Base02 — dark text for contrast on light
|
||||
PROMPT_COLOR_OK_MARK="\e[38;2;133;153;0m" # Green — checkmark
|
||||
|
||||
PROMPT_COLOR_ERR_BG="\e[48;2;220;50;47m" # Red — error background
|
||||
PROMPT_COLOR_ERR_FG="\e[38;2;253;246;227m" # Base3 — light text on red
|
||||
PROMPT_COLOR_ERR_MARK="\e[38;2;253;246;227m" # Base3 — X mark (bright on red)
|
||||
|
||||
PROMPT_COLOR_ROOT_FG="\e[38;2;220;50;47m" # Red — root warning
|
||||
PROMPT_COLOR_USER_FG="\e[38;2;42;161;152m" # Cyan — normal user
|
||||
PROMPT_COLOR_DIR_FG="\e[38;2;38;139;210m" # Blue — working directory
|
||||
122
profile.d/themes/solarized.theme
Normal file
122
profile.d/themes/solarized.theme
Normal file
@@ -0,0 +1,122 @@
|
||||
# Solarized Dark prompt theme — exact 24-bit / true-colour palette
|
||||
# ------------------------------------------------------------------------------
|
||||
# Theme files are NOT executed as shell scripts. load_theme() parses them
|
||||
# line by line. Only the following value forms are accepted:
|
||||
#
|
||||
# KEY="$ColorVarName" — reference to a colour variable from disp.sh
|
||||
# KEY="${ColorVarName}" — same with braces
|
||||
# KEY="\e[...m" — raw ANSI escape sequence (single block)
|
||||
#
|
||||
# Accepted keys:
|
||||
# PROMPT_COLOR_* — prompt slot colours (see profile.conf [prompt])
|
||||
# Standard colour vars — Black, Blue, On_IBlack, … (overrides the palette
|
||||
# from disp.sh for the whole shell session)
|
||||
#
|
||||
# Any unknown key, unsafe value, or shell construct will be ignored with a
|
||||
# warning — theme files cannot execute code.
|
||||
# ------------------------------------------------------------------------------
|
||||
# Requires a terminal with true-colour / 24-bit support.
|
||||
# Check with: printf '\e[38;2;220;50;47mred\e[0m\n'
|
||||
# If you see solid red text, your terminal supports this theme.
|
||||
# (konsole, iTerm2, kitty, alacritty, Windows Terminal all do)
|
||||
#
|
||||
# Solarized Dark exact hex → RGB mapping used below:
|
||||
# Base03 #002b36 → 0 43 54 (darkest background)
|
||||
# Base02 #073642 → 7 54 66 (background highlights)
|
||||
# Base01 #586e75 → 88 110 117 (comments / secondary content)
|
||||
# Base00 #657b83 →101 123 131 (body text — dark background)
|
||||
# Base0 #839496 →131 148 150 (body text — main)
|
||||
# Base1 #93a1a1 →147 161 161 (optional emphasis)
|
||||
# Yellow #b58900 →181 137 0
|
||||
# Orange #cb4b16 →203 75 22
|
||||
# Red #dc322f →220 50 47
|
||||
# Magenta #d33682 →211 54 130
|
||||
# Violet #6c71c4 →108 113 196
|
||||
# Blue #268bd2 → 38 139 210
|
||||
# Cyan #2aa198 → 42 161 152
|
||||
# Green #859900 →133 153 0
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# ---- Foreground palette overrides -------------------------------------------
|
||||
# Regular
|
||||
Black="\e[38;2;0;43;54m" # Base03 — darkest content fg
|
||||
Red="\e[38;2;220;50;47m" # Red
|
||||
Green="\e[38;2;133;153;0m" # Green
|
||||
Yellow="\e[38;2;181;137;0m" # Yellow — primary accent
|
||||
Blue="\e[38;2;38;139;210m" # Blue
|
||||
Purple="\e[38;2;211;54;130m" # Magenta
|
||||
Cyan="\e[38;2;42;161;152m" # Cyan
|
||||
White="\e[38;2;131;148;150m" # Base0 — body text
|
||||
|
||||
# Bold
|
||||
BBlack="\e[1;38;2;7;54;66m" # Base02 bold
|
||||
BRed="\e[1;38;2;220;50;47m" # Red bold
|
||||
BGreen="\e[1;38;2;133;153;0m" # Green bold
|
||||
BYellow="\e[1;38;2;181;137;0m" # Yellow bold
|
||||
BBlue="\e[1;38;2;38;139;210m" # Blue bold
|
||||
BPurple="\e[1;38;2;211;54;130m" # Magenta bold
|
||||
BCyan="\e[1;38;2;42;161;152m" # Cyan bold
|
||||
BWhite="\e[1;38;2;147;161;161m" # Base1 bold
|
||||
|
||||
# High intensity (brighter / emphasis roles in Solarized)
|
||||
IBlack="\e[38;2;88;110;117m" # Base01 — secondary/comments
|
||||
IRed="\e[38;2;203;75;22m" # Orange — Solarized's "bright red"
|
||||
IGreen="\e[38;2;133;153;0m" # Green (no brighter variant)
|
||||
IYellow="\e[38;2;181;137;0m" # Yellow (no brighter variant)
|
||||
IBlue="\e[38;2;108;113;196m" # Violet — Solarized's "bright blue"
|
||||
IPurple="\e[38;2;211;54;130m" # Magenta (no brighter variant)
|
||||
ICyan="\e[38;2;42;161;152m" # Cyan (no brighter variant)
|
||||
IWhite="\e[38;2;147;161;161m" # Base1 — optional emphasis
|
||||
|
||||
# Bold high intensity
|
||||
BIBlack="\e[1;38;2;88;110;117m" # Base01 bold
|
||||
BIRed="\e[1;38;2;203;75;22m" # Orange bold
|
||||
BIGreen="\e[1;38;2;133;153;0m" # Green bold
|
||||
BIYellow="\e[1;38;2;181;137;0m" # Yellow bold
|
||||
BIBlue="\e[1;38;2;108;113;196m" # Violet bold
|
||||
BIPurple="\e[1;38;2;211;54;130m" # Magenta bold
|
||||
BICyan="\e[1;38;2;42;161;152m" # Cyan bold
|
||||
BIWhite="\e[1;38;2;147;161;161m" # Base1 bold
|
||||
|
||||
# ---- Background palette overrides -------------------------------------------
|
||||
On_Black="\e[48;2;0;43;54m" # Base03
|
||||
On_Red="\e[48;2;220;50;47m" # Red
|
||||
On_Green="\e[48;2;133;153;0m" # Green
|
||||
On_Yellow="\e[48;2;181;137;0m" # Yellow
|
||||
On_Blue="\e[48;2;38;139;210m" # Blue
|
||||
On_Purple="\e[48;2;211;54;130m" # Magenta
|
||||
On_Cyan="\e[48;2;42;161;152m" # Cyan
|
||||
On_White="\e[48;2;131;148;150m" # Base0
|
||||
|
||||
On_IBlack="\e[48;2;7;54;66m" # Base02 — background highlights
|
||||
On_IRed="\e[48;2;203;75;22m" # Orange
|
||||
On_IGreen="\e[48;2;133;153;0m" # Green
|
||||
On_IYellow="\e[48;2;181;137;0m" # Yellow
|
||||
On_IBlue="\e[48;2;108;113;196m" # Violet
|
||||
On_IPurple="\e[48;2;211;54;130m" # Magenta
|
||||
On_ICyan="\e[48;2;42;161;152m" # Cyan
|
||||
On_IWhite="\e[48;2;147;161;161m" # Base1
|
||||
|
||||
# ---- Special codes ----------------------------------------------------------
|
||||
DEFAULTFG="\e[38;2;131;148;150m" # Base0 — default foreground
|
||||
DEFAULTBG="\e[48;2;0;43;54m" # Base03 — default background
|
||||
RESETCOL="\e[0m"
|
||||
|
||||
# ---- Prompt colour slots ----------------------------------------------------
|
||||
# All specified as direct ANSI sequences to avoid ordering dependencies
|
||||
# with the palette overrides above.
|
||||
|
||||
PROMPT_COLOR_TIME_FG="\e[38;2;181;137;0m" # Yellow — primary accent
|
||||
PROMPT_COLOR_TIME_BG="\e[48;2;0;43;54m" # Base03 — darkest background
|
||||
PROMPT_COLOR_BAR_BG="\e[48;2;88;110;117m" # Base01 — slightly brighter bar (was Base02)
|
||||
|
||||
PROMPT_COLOR_OK_FG="\e[38;2;131;148;150m" # Base0 — body text on success
|
||||
PROMPT_COLOR_OK_MARK="\e[38;2;133;153;0m" # Green — checkmark
|
||||
|
||||
PROMPT_COLOR_ERR_BG="\e[48;2;180;20;15m" # deeper crimson — more contrast than Solarized Red
|
||||
PROMPT_COLOR_ERR_FG="\e[1;38;2;255;255;255m" # bold pure white — maximum contrast on dark red
|
||||
PROMPT_COLOR_ERR_MARK="\e[1;38;2;253;246;227m" # Base3 bold — bright warm mark stands out on crimson
|
||||
|
||||
PROMPT_COLOR_ROOT_FG="\e[38;2;220;50;47m" # Red — root warning
|
||||
PROMPT_COLOR_USER_FG="\e[38;2;42;161;152m" # Cyan — normal user
|
||||
PROMPT_COLOR_DIR_FG="\e[38;2;38;139;210m" # Blue — working directory
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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,
|
||||
@@ -35,99 +35,390 @@
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
export BASE_URL="https://git.geoffray-levasseur.org/fatalerrors/profile"
|
||||
export UPDT_URL="$BASE_URL/raw/branch/master"
|
||||
export ARCH_URL="$BASE_URL/archive/master.tar.gz"
|
||||
|
||||
# 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 for profile updates
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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()
|
||||
{
|
||||
if [[ $1 == "-q" ]]; then
|
||||
# Quiet mode is mostly used internally when profile_upgrade is called
|
||||
quiet=1
|
||||
local quiet=0 result=5 PARSED
|
||||
local vfile="" lastver=""
|
||||
|
||||
PARSED=$(getopt -o hq --long help,quiet -n 'check_updates' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"check_updates --help\" to display usage."
|
||||
return 2
|
||||
fi
|
||||
[[ -n $quiet ]] && disp I "Checking for updates..."
|
||||
local vfile="/tmp/version"
|
||||
wget "$UPDT_URL/version" -O $vfile >/dev/null 2>&1 || {
|
||||
disp E "Can't download version file, impossible to proceed!"
|
||||
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
|
||||
}
|
||||
|
||||
# In quiet mode (startup), use a short timeout so a missing or slow network
|
||||
# never blocks the interactive prompt.
|
||||
local dwl_opts=()
|
||||
(( quiet == 1 )) && dwl_opts+=(-t 3)
|
||||
|
||||
dwl "${dwl_opts[@]}" "$UPDT_URL/version" "$vfile" >/dev/null 2>&1 || {
|
||||
rm -f "$vfile"
|
||||
(( quiet != 1 )) && disp E "Cannot download version file; unable to continue."
|
||||
return 5
|
||||
}
|
||||
|
||||
if [[ -s $vfile ]]; then
|
||||
local lastver=$(cat $vfile)
|
||||
if [[ $lastver != $PROFVERSION ]]; then
|
||||
disp I "You have version $PROFVERSION installed. Version $lastver is available."
|
||||
[[ $quiet ]] && disp I "You should upgrade to last version when possible."
|
||||
lastver=$(<"$vfile")
|
||||
if [[ "$lastver" != "$PROFVERSION" ]]; then
|
||||
disp I "Installed: $PROFVERSION. Available: $lastver."
|
||||
(( quiet != 1 )) && disp I "You should upgrade when possible."
|
||||
result=1
|
||||
else
|
||||
[[ -n $quiet ]] && disp I "Your version is up-to-date."
|
||||
(( quiet != 1 )) && disp I "Your version is up-to-date."
|
||||
result=0
|
||||
fi
|
||||
rm -f $vfile
|
||||
rm -f "$vfile"
|
||||
else
|
||||
disp E "Impossible to read temporary file, impossible to proceed."
|
||||
rm -f "$vfile"
|
||||
disp E "Temporary file is unreadable; unable to continue."
|
||||
fi
|
||||
unset lastver vfile
|
||||
|
||||
return $result
|
||||
}
|
||||
export -f check_updates
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Apply update to profile
|
||||
# ------------------------------------------------------------------------------
|
||||
# Apply the available profile upgrade
|
||||
# Usage: profile_upgrade [options]
|
||||
profile_upgrade()
|
||||
{
|
||||
if check_updates -q; then
|
||||
disp "No update available."
|
||||
local PARSED
|
||||
local check_rc=0 dry_run=0 force_git=0 switch_to_git=0
|
||||
local archive_file="" tmpbase="" use_archive=0 branch=""
|
||||
local tmpdir="" archive="" extracted_root=""
|
||||
|
||||
PARSED=$(getopt -o hf:t:nFb:g --long help,file:,tmpdir:,dry-run,force,branch:,switch-to-git -n 'profile_upgrade' -- "$@")
|
||||
# shellcheck disable=SC2181 # getopt return code is checked immediately after
|
||||
if [[ $? -ne 0 ]]; then
|
||||
disp E "Invalid options, use \"profile_upgrade --help\" to display usage."
|
||||
return 2
|
||||
fi
|
||||
eval set -- "$PARSED"
|
||||
while true; do
|
||||
case "$1" in
|
||||
-h|--help)
|
||||
printf "profile_upgrade: Apply the available profile upgrade.\n\n"
|
||||
printf "Usage: profile_upgrade [options]\n\n"
|
||||
printf "Options:\n"
|
||||
printf "\t-h, --help\t\tDisplay this help screen\n"
|
||||
printf "\t-f, --file ARCHIVE\tUse a local archive file for the upgrade\n"
|
||||
printf "\t-t, --tmpdir DIR\tCreate the temporary working directory under DIR\n"
|
||||
printf "\t-b, --branch NAME\tUse NAME as the target Git branch\n"
|
||||
printf "\t-g, --switch-to-git\tReplace current install with a fresh Git clone\n"
|
||||
printf "\t-n, --dry-run\t\tDisplay what would be done without changing anything\n"
|
||||
printf "\t-F, --force\t\tDiscard local changes before upgrading\n\n"
|
||||
printf "If the profile is installed from Git, the upgrade uses 'git pull'.\n"
|
||||
printf "Otherwise, it downloads or applies an archive and refreshes the files.\n"
|
||||
return 0
|
||||
;;
|
||||
-f|--file)
|
||||
archive_file="$2"
|
||||
use_archive=1
|
||||
shift 2
|
||||
;;
|
||||
-t|--tmpdir)
|
||||
tmpbase="$2"
|
||||
shift 2
|
||||
;;
|
||||
-b|--branch)
|
||||
branch="$2"
|
||||
shift 2
|
||||
;;
|
||||
-g|--switch-to-git)
|
||||
switch_to_git=1
|
||||
shift
|
||||
;;
|
||||
-n|--dry-run)
|
||||
dry_run=1
|
||||
shift
|
||||
;;
|
||||
-F|--force)
|
||||
force_git=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
disp E "Invalid options, use \"profile_upgrade --help\" to display usage."
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( ! use_archive && ! switch_to_git )); then
|
||||
check_updates -q
|
||||
check_rc=$?
|
||||
if (( check_rc == 0 )); then
|
||||
disp I "No update available."
|
||||
return 0
|
||||
elif (( check_rc > 1 )); then
|
||||
disp E "Unable to check whether an update is available."
|
||||
return "$check_rc"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -s $MYPATH/profile.sh ]]; then
|
||||
disp E "Installation path detection failed, cannot upgrade automatically."
|
||||
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."
|
||||
local curdir=$(pwd)
|
||||
cd $MYPATH
|
||||
git pull || {
|
||||
disp E "Git pull failed, upgrade not applyed."
|
||||
cd "$curdir"
|
||||
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."
|
||||
cd "$curdir"
|
||||
fi
|
||||
popd >/dev/null || return 1
|
||||
else
|
||||
disp I "No Git detected. Downloading and applying upgrade from archive..."
|
||||
local tmpdir="/tmp/profile_upg.$$"
|
||||
mkdir -p "$tmpdir" || {
|
||||
disp E "Failed to create temporary directory."
|
||||
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
|
||||
|
||||
local archive="$tmpdir/profile.tar.gz"
|
||||
wget -q "$ARCH_URL" -O "$archive" || {
|
||||
disp E "Failed to download archive."
|
||||
rm -rf "$tmpdir"
|
||||
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
|
||||
|
||||
tar -xzf "$archive" -C "$tmpdir" || {
|
||||
disp E "Archive extraction failed."
|
||||
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
|
||||
|
||||
disp I "Installing new version..."
|
||||
cp -r "$tmpdir"/profile/* "$MYPATH"/ || {
|
||||
disp E "Failed to copy new files to $MYPATH."
|
||||
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
|
||||
}
|
||||
|
||||
disp I "Upgrade complete. You should now logout and login again."
|
||||
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
|
||||
|
||||
399
profile.sh
399
profile.sh
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env bash
|
||||
# Begin profile
|
||||
# ------------------------------------------------------------------------------
|
||||
# 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,
|
||||
@@ -35,42 +35,280 @@
|
||||
# * OF SUCH DAMAGE.
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
if [[ ! $SHELL =~ bash|zsh ]]; then
|
||||
echo "That environment script is designed to be used with bash or zsh being the shell."
|
||||
echo "Please consider using bash or zsh instead, or patch me ;)!"
|
||||
_profile_is_sourced()
|
||||
{
|
||||
[[ "${BASH_SOURCE[0]}" != "$0" ]]
|
||||
}
|
||||
|
||||
_profile_finish()
|
||||
{
|
||||
local rc="${1:-0}"
|
||||
if _profile_is_sourced; then
|
||||
return "$rc"
|
||||
fi
|
||||
exit "$rc"
|
||||
}
|
||||
|
||||
_profile_install_in_file()
|
||||
{
|
||||
local rc_file="$1"
|
||||
local source_line="$2"
|
||||
|
||||
[[ -f "$rc_file" ]] || touch "$rc_file" || {
|
||||
printf "[ Error ] Cannot create %s\n" "$rc_file" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
if grep -Fqx "$source_line" "$rc_file"; then
|
||||
printf "[ Info ] Already configured in %s\n" "$rc_file"
|
||||
return 0
|
||||
fi
|
||||
|
||||
printf "\n%s\n" "$source_line" >> "$rc_file" || {
|
||||
printf "[ Error ] Cannot write to %s\n" "$rc_file" >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
printf "[ Info ] Added profile source line to %s\n" "$rc_file"
|
||||
return 0
|
||||
}
|
||||
|
||||
_profile_install()
|
||||
{
|
||||
local install_bashrc=0
|
||||
local install_profile=0
|
||||
local target_selected=0
|
||||
local script_dir source_line rc=0
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--bashrc)
|
||||
install_bashrc=1
|
||||
target_selected=1
|
||||
;;
|
||||
--profile)
|
||||
install_profile=1
|
||||
target_selected=1
|
||||
;;
|
||||
-h|--help)
|
||||
printf "Usage: %s --install [--bashrc] [--profile]\n" "${BASH_SOURCE[0]}"
|
||||
printf "If no target is specified, both ~/.bashrc and ~/.profile are configured.\n"
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
printf "[ Error ] Unknown install option: %s\n" "$1" >&2
|
||||
return 2
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if (( target_selected == 0 )); then
|
||||
install_bashrc=1
|
||||
install_profile=1
|
||||
fi
|
||||
|
||||
script_dir=$(dirname "$(realpath -s "${BASH_SOURCE[0]}")")
|
||||
source_line="source \"$script_dir/profile.sh\""
|
||||
|
||||
if (( install_bashrc == 1 )); then
|
||||
_profile_install_in_file "$HOME/.bashrc" "$source_line" || rc=$?
|
||||
fi
|
||||
|
||||
if (( install_profile == 1 )); then
|
||||
_profile_install_in_file "$HOME/.profile" "$source_line" || rc=$?
|
||||
fi
|
||||
|
||||
return "$rc"
|
||||
}
|
||||
|
||||
if [[ $# -gt 0 ]]; then
|
||||
case "$1" in
|
||||
--install)
|
||||
shift
|
||||
_profile_install "$@"
|
||||
_profile_finish $?
|
||||
;;
|
||||
-h|--help)
|
||||
printf "Usage: source %s\n" "${BASH_SOURCE[0]}"
|
||||
printf " %s --install [--bashrc] [--profile]\n" "${BASH_SOURCE[0]}"
|
||||
_profile_finish 0
|
||||
;;
|
||||
*)
|
||||
printf "[ Error ] Unknown option: %s\n" "$1" >&2
|
||||
_profile_finish 2
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if ! _profile_is_sourced; then
|
||||
printf "[ Warning ] profile.sh is designed to be sourced, not executed directly.\n" >&2
|
||||
printf "Use: source \"%s\"\n" "$(realpath -s "${BASH_SOURCE[0]}")" >&2
|
||||
printf "Or run: %s --install [--bashrc] [--profile]\n" "${BASH_SOURCE[0]}" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
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!"
|
||||
_profile_finish 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 0 2>/dev/null) && return 1 || exit 1
|
||||
fi
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# path* : private functions for PATH variable management
|
||||
# ------------------------------------------------------------------------------
|
||||
pathremove()
|
||||
{
|
||||
[[ -z "$1" ]] && return 0
|
||||
local IFS=':'
|
||||
local newpath
|
||||
local dir
|
||||
local pathvar=${2:-PATH}
|
||||
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
|
||||
if [ "$dir" != "$1" ]; then
|
||||
newpath=${newpath:+$newpath:}$dir
|
||||
fi
|
||||
[[ "$dir" != "$1" ]] && newpath="${newpath:+$newpath:}$dir"
|
||||
done
|
||||
export $pathvar="$newpath"
|
||||
export "$pathvar=$newpath"
|
||||
}
|
||||
|
||||
pathprepend()
|
||||
{
|
||||
pathremove $1 $2
|
||||
local pathvar=${2:-PATH}
|
||||
export $pathvar="$1${!pathvar:+:${!pathvar}}"
|
||||
}
|
||||
#pathprepend() # Unused for now, but might be useful in the future
|
||||
#{
|
||||
# [[ -z "$1" ]] && return 0
|
||||
# local pathvar="${2:-PATH}"
|
||||
# [[ "$pathvar" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] || {
|
||||
# printf "pathprepend: unsafe variable name '%s'\n" "$pathvar" >&2
|
||||
# return 1
|
||||
# }
|
||||
# pathremove "$1" "$pathvar"
|
||||
# export "$pathvar=$1${!pathvar:+:${!pathvar}}"
|
||||
#}
|
||||
|
||||
pathappend()
|
||||
{
|
||||
pathremove $1 $2
|
||||
local pathvar=${2:-PATH}
|
||||
export $pathvar="${!pathvar:+${!pathvar}:}$1"
|
||||
[[ -z "$1" ]] && return 0
|
||||
local pathvar="${2:-PATH}"
|
||||
[[ "$pathvar" =~ ^[a-zA-Z_][a-zA-Z0-9_]*$ ]] || {
|
||||
printf "pathappend: unsafe variable name '%s'\n" "$pathvar" >&2
|
||||
return 1
|
||||
}
|
||||
pathremove "$1" "$pathvar"
|
||||
export "$pathvar=${!pathvar:+${!pathvar}:}$1"
|
||||
}
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Configuration file parser
|
||||
parse_conf()
|
||||
{
|
||||
local config_file="$1"
|
||||
local current_section=""
|
||||
local key value
|
||||
|
||||
[[ ! -f "$config_file" ]] && return 1
|
||||
|
||||
while IFS='=' read -r key value || [[ -n "$key" ]]; do
|
||||
# Internal trimming (removes leading/trailing whitespace & CR)
|
||||
key="${key%"${key##*[![:space:]]}"}"
|
||||
key="${key#"${key%%[![:space:]]*}"}"
|
||||
key="${key%$'\r'}" # Strip potential Windows line endings
|
||||
|
||||
# Skip comments and empty lines
|
||||
[[ -z "$key" || "$key" =~ ^[#\;] ]] && continue
|
||||
|
||||
# Section Detection: [section_name]
|
||||
if [[ "$key" =~ ^\[([a-zA-Z0-9_]+)\]$ ]]; then
|
||||
current_section="${BASH_REMATCH[1]}"
|
||||
declare -g -A "CONF_$current_section"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Secure Assignment (if inside a section)
|
||||
if [[ -n "$current_section" ]]; then
|
||||
# Clean the value
|
||||
value="${value%"${value##*[![:space:]]}"}"
|
||||
value="${value#"${value%%[![:space:]]*}"}"
|
||||
value="${value%$'\r'}"
|
||||
|
||||
# Protect against command injection by disallowing certain characters in keys
|
||||
value="${value//\`/}"
|
||||
value="${value//\$\(/}"
|
||||
|
||||
# Correctly interpretet internal variables (e.g. $HOME)
|
||||
if [[ "$value" == *\$* ]]; then
|
||||
value=$(envsubst <<< "$value")
|
||||
fi
|
||||
|
||||
# Strip quotes (handling both " and ')
|
||||
value="${value%\"}"; value="${value#\"}"
|
||||
value="${value%\'}"; value="${value#\'}"
|
||||
|
||||
# Use a nameref for safe, eval-free assignment
|
||||
local -n current_array="CONF_$current_section"
|
||||
# shellcheck disable=SC2034 # Dynamic var creation
|
||||
current_array["$key"]="$value"
|
||||
fi
|
||||
done < "$config_file"
|
||||
}
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Load command aliases from configuration
|
||||
load_alias()
|
||||
{
|
||||
local section_name="CONF_$1"
|
||||
|
||||
# Check if the associative array exists using declare -p
|
||||
[[ "$(declare -p "$section_name" 2>/dev/null)" != "declare -A"* ]] && return 1
|
||||
|
||||
# Create a nameref to the section array
|
||||
local -n current_aliases="$section_name"
|
||||
|
||||
# Iterate safely over the keys of the associative array
|
||||
for key in "${!current_aliases[@]}"; do
|
||||
local cmd="${current_aliases[$key]}"
|
||||
|
||||
# Extract the base command (first word) safely without awk
|
||||
local base_cmd="${cmd%% *}"
|
||||
|
||||
# Only alias if the base command is executable
|
||||
if command -v "$base_cmd" >/dev/null 2>&1; then
|
||||
# shellcheck disable=SC2139 # Dynamic alias creation
|
||||
alias "$key"="$cmd"
|
||||
fi
|
||||
done
|
||||
}
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Load configuration values as environment variables
|
||||
load_conf()
|
||||
{
|
||||
local section_name="CONF_$1"
|
||||
|
||||
# Missing section is not an error: modules can rely on built-in defaults.
|
||||
[[ "$(declare -p "$section_name" 2>/dev/null)" != "declare -A"* ]] && return 0
|
||||
|
||||
local -n current_vars="$section_name"
|
||||
|
||||
for key in "${!current_vars[@]}"; do
|
||||
# Export the key/value pair as a standard shell variable
|
||||
# We use 'export' directly; Bash handles the assignment safely here
|
||||
export "$key"="${current_vars[$key]}"
|
||||
done
|
||||
}
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------
|
||||
@@ -79,70 +317,54 @@ 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 "$0")")
|
||||
MYPATH=$(dirname "$(realpath -s "${BASH_SOURCE[0]}")")
|
||||
else
|
||||
export MYPATH="$PROFILE_PATH"
|
||||
MYPATH="$PROFILE_PATH"
|
||||
fi
|
||||
export MYPATH
|
||||
if [[ ! -e "$MYPATH/profile.sh" ]]; then
|
||||
echo "[ Warning ] Path detection failed, trying to use pwd..."
|
||||
MYPATH=$(pwd)
|
||||
if [[ ! -e "$MYPATH/profile.sh" ]]; then
|
||||
echo "[ Error ] Impossible to determine installation path, pretty much nothing will work."
|
||||
echo "[ Error ] Unable to determine installation path, pretty much nothing will work."
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -s "$MYPATH/version" ]]; then
|
||||
echo "[ Warning ] Impossible to determine running version of profile, your installation might be broken."
|
||||
echo "[ Warning ] Unable to determine running profile version; your installation might be broken."
|
||||
fi
|
||||
export PROFVERSION=$(cat "$MYPATH"/version)
|
||||
PROFVERSION=$(cat "$MYPATH"/version)
|
||||
export PROFVERSION
|
||||
|
||||
# Build PATH environment variable
|
||||
if [[ $EUID -eq 0 ]]; then
|
||||
pathappend /sbin:/usr/sbin
|
||||
fi
|
||||
[[ -d /share/services/gestparc ]] && pathappend /share/services/gestparc
|
||||
[[ -d ~/bin ]] && pathappend ~/bin
|
||||
[[ -d ~/.local/bin ]] && pathappend ~/.local/bin
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Default values are set here and will be overloaded with config file if any
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Set bash history
|
||||
export HISTSIZE=50000
|
||||
export HISTIGNORE="&:[bf]g:exit"
|
||||
|
||||
# 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 city for weather forcast
|
||||
export DEFAULT_CITY="Toulouse"
|
||||
|
||||
# ------------------------------------------------------------------------------
|
||||
# Default values could be altered after this line
|
||||
# ------------------------------------------------------------------------------
|
||||
|
||||
# Load global configuration
|
||||
[[ -f $MYPATH/etc/profile.conf ]] && . $MYPATH/etc/profile.conf
|
||||
|
||||
# Load personal configuration
|
||||
[[ -f ~/.profile.conf ]] && . ~/.profile.conf
|
||||
# Parse and load general configuration
|
||||
export PROFILE_CONF="$MYPATH/profile.conf"
|
||||
parse_conf "$PROFILE_CONF"
|
||||
# Overload with user configuration if it exists
|
||||
if [[ -f "$HOME/.profile.conf" ]]; then
|
||||
parse_conf "$HOME/.profile.conf"
|
||||
fi
|
||||
load_conf system # Load Bash system behavior configuration (history, pager, etc.)
|
||||
load_conf general # General purpose configuration (compilation flags, etc.)
|
||||
|
||||
# Load module scripts
|
||||
for script in $MYPATH/profile.d/*.sh; do
|
||||
if [[ -r $script ]]; then
|
||||
. $script
|
||||
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
|
||||
@@ -150,46 +372,43 @@ done
|
||||
[[ $- == *i* ]] && export INTERACTIVE=1
|
||||
|
||||
if [[ $INTERACTIVE ]]; then
|
||||
# Load custom bash completions
|
||||
shopt -s nullglob
|
||||
for _compl in "$MYPATH/profile.d/bash-completion/"*.sh; do
|
||||
[[ -f "$_compl" && -r "$_compl" ]] && . "$_compl"
|
||||
done
|
||||
unset _compl
|
||||
shopt -u nullglob
|
||||
|
||||
# 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'
|
||||
alias hdu='du -hs ./* | sort -H'
|
||||
load_alias aliases
|
||||
|
||||
# Define PS1
|
||||
trap 'timer_start' DEBUG
|
||||
PROMPT_COMMAND='set_prompt'
|
||||
|
||||
# Set default language
|
||||
setfr
|
||||
showinfo
|
||||
# Set default language from DEFAULT_LANG config key (set in [general]).
|
||||
# The value must match one of the alias names defined in SET_LOCALE so that
|
||||
# the corresponding set<alias> function exists after build_locale_shortcuts.
|
||||
if [[ -n "${DEFAULT_LANG:-}" ]]; then
|
||||
_lang_fn="set${DEFAULT_LANG}"
|
||||
if declare -F "$_lang_fn" >/dev/null 2>&1; then
|
||||
"$_lang_fn"
|
||||
else
|
||||
disp W "DEFAULT_LANG '$DEFAULT_LANG' has no matching locale shortcut (check SET_LOCALE in profile.conf)."
|
||||
fi
|
||||
unset _lang_fn
|
||||
fi
|
||||
showinfo && printf "\n"
|
||||
check_updates -q
|
||||
disp I "Profile version $PROFVERSION chargé..."
|
||||
disp I "Profile version $PROFVERSION loaded..."
|
||||
fi
|
||||
|
||||
# Cleanup
|
||||
unset pathremove pathprepend pathappend
|
||||
unset -f _profile_is_sourced _profile_finish _profile_install_in_file _profile_install
|
||||
unset -f parse_conf load_alias load_conf
|
||||
unset -f pathremove pathprepend pathappend
|
||||
|
||||
#return 0
|
||||
|
||||
|
||||
Reference in New Issue
Block a user