Compare commits
No commits in common. "f905de1d2c319286563a544123d7344600799800" and "df102bce40ecd77851acdac3930b5019f9383ab9" have entirely different histories.
f905de1d2c
...
df102bce40
3 changed files with 259 additions and 508 deletions
|
|
@ -406,7 +406,6 @@ with lib;
|
||||||
gnupg
|
gnupg
|
||||||
gron
|
gron
|
||||||
guestfs-tools
|
guestfs-tools
|
||||||
gurk-rs
|
|
||||||
htop
|
htop
|
||||||
ijq
|
ijq
|
||||||
iperf3
|
iperf3
|
||||||
|
|
@ -425,7 +424,6 @@ with lib;
|
||||||
mosh
|
mosh
|
||||||
mpv
|
mpv
|
||||||
mupdf
|
mupdf
|
||||||
nautilus
|
|
||||||
neomutt
|
neomutt
|
||||||
netbird-ui
|
netbird-ui
|
||||||
netcat-openbsd
|
netcat-openbsd
|
||||||
|
|
@ -439,7 +437,6 @@ with lib;
|
||||||
openvpn
|
openvpn
|
||||||
pangolin-cli
|
pangolin-cli
|
||||||
paprefs
|
paprefs
|
||||||
parted
|
|
||||||
patchelf
|
patchelf
|
||||||
pavucontrol
|
pavucontrol
|
||||||
pciutils
|
pciutils
|
||||||
|
|
@ -455,6 +452,7 @@ with lib;
|
||||||
signal-cli
|
signal-cli
|
||||||
signal-cli
|
signal-cli
|
||||||
signal-desktop
|
signal-desktop
|
||||||
|
gurk-rs
|
||||||
simplex-chat-desktop
|
simplex-chat-desktop
|
||||||
slack
|
slack
|
||||||
spacer
|
spacer
|
||||||
|
|
@ -482,13 +480,8 @@ with lib;
|
||||||
yq
|
yq
|
||||||
yt-dlp
|
yt-dlp
|
||||||
yubioath-flutter
|
yubioath-flutter
|
||||||
|
nautilus
|
||||||
(pkgs.writeShellScriptBin "kpxc-search" ''
|
parted
|
||||||
export GI_TYPELIB_PATH=${pkgs.lib.makeSearchPath "lib/girepository-1.0" [
|
|
||||||
pkgs.at-spi2-core pkgs.glib.out pkgs.gobject-introspection
|
|
||||||
]}''${GI_TYPELIB_PATH:+:$GI_TYPELIB_PATH}
|
|
||||||
exec ${pkgs.python3.withPackages (ps: [ ps.pygobject3 ])}/bin/python3 ${./scripts/kpxc-search} "$@"
|
|
||||||
'')
|
|
||||||
];
|
];
|
||||||
programs.ydotool.enable = true;
|
programs.ydotool.enable = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,239 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
"""
|
|
||||||
kpxc-search: put a query into the search box of an already running KeePassXC
|
|
||||||
via AT-SPI (the Linux accessibility bus). Works on X11 and Wayland.
|
|
||||||
|
|
||||||
Usage: kpxc-search 'u:alice url:ssh://db01.example.com' [--timeout 60] [--debug]
|
|
||||||
|
|
||||||
Requirements:
|
|
||||||
- PyGObject + the Atspi typelib (at-spi2-core)
|
|
||||||
- KeePassXC started with QT_LINUX_ACCESSIBILITY_ALWAYS_ON=1
|
|
||||||
|
|
||||||
Speed: every AT-SPI property read is a D-Bus round trip, so walking the widget
|
|
||||||
tree is the slow part. After the first successful run the script remembers the
|
|
||||||
search box's position in the tree (in $XDG_RUNTIME_DIR/kpxc-search.json) and
|
|
||||||
goes straight there next time, verifying it is still the right widget. It falls
|
|
||||||
back to the full walk only if that check fails (layout changed, new version...).
|
|
||||||
|
|
||||||
Exit codes: 0 = query set, 1 = KeePassXC not found, 2 = search box not found
|
|
||||||
(e.g. database still locked and --timeout expired).
|
|
||||||
"""
|
|
||||||
import argparse
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
import gi
|
|
||||||
|
|
||||||
gi.require_version("Atspi", "2.0")
|
|
||||||
from gi.repository import Atspi, GLib # noqa: E402
|
|
||||||
|
|
||||||
# Never descend into these: the entry/group views expose one accessible object
|
|
||||||
# per cell, and walking them over D-Bus is what makes a naive search crawl.
|
|
||||||
PRUNE_ROLES = {
|
|
||||||
Atspi.Role.TABLE,
|
|
||||||
Atspi.Role.TREE_TABLE,
|
|
||||||
Atspi.Role.TREE,
|
|
||||||
Atspi.Role.LIST,
|
|
||||||
Atspi.Role.LIST_BOX,
|
|
||||||
Atspi.Role.TABLE_CELL,
|
|
||||||
Atspi.Role.TREE_ITEM,
|
|
||||||
Atspi.Role.LIST_ITEM,
|
|
||||||
Atspi.Role.MENU_BAR,
|
|
||||||
Atspi.Role.MENU,
|
|
||||||
}
|
|
||||||
|
|
||||||
# Qt reports a QLineEdit as ENTRY on some versions and as TEXT on others.
|
|
||||||
TEXT_ROLES = {Atspi.Role.ENTRY, Atspi.Role.TEXT}
|
|
||||||
|
|
||||||
CACHE_FILE = (
|
|
||||||
Path(os.environ.get("XDG_RUNTIME_DIR") or f"/tmp/kpxc-search-{os.getuid()}")
|
|
||||||
/ "kpxc-search.json"
|
|
||||||
)
|
|
||||||
|
|
||||||
DEBUG = False
|
|
||||||
|
|
||||||
|
|
||||||
def log(msg_fn):
|
|
||||||
# Takes a callable so the D-Bus calls inside debug messages only happen
|
|
||||||
# when --debug is on.
|
|
||||||
if DEBUG:
|
|
||||||
print(msg_fn(), file=sys.stderr)
|
|
||||||
|
|
||||||
|
|
||||||
def safe(fn, default=None):
|
|
||||||
try:
|
|
||||||
return fn()
|
|
||||||
except GLib.Error:
|
|
||||||
return default
|
|
||||||
|
|
||||||
|
|
||||||
def child_at(node, i):
|
|
||||||
return safe(lambda: node.get_child_at_index(i))
|
|
||||||
|
|
||||||
|
|
||||||
def children(node):
|
|
||||||
n = safe(node.get_child_count, 0) or 0
|
|
||||||
for i in range(n):
|
|
||||||
child = child_at(node, i)
|
|
||||||
if child is not None:
|
|
||||||
yield i, child
|
|
||||||
|
|
||||||
|
|
||||||
def is_line_edit(role, states):
|
|
||||||
return (
|
|
||||||
role in TEXT_ROLES
|
|
||||||
and states is not None
|
|
||||||
and states.contains(Atspi.StateType.SHOWING)
|
|
||||||
and states.contains(Atspi.StateType.EDITABLE)
|
|
||||||
and not states.contains(Atspi.StateType.MULTI_LINE)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def walk(node, ancestors=(), path=(), depth=0, max_depth=30):
|
|
||||||
"""Yield (node, role, states, ancestor_roles, index_path) over the visible,
|
|
||||||
non-pruned part of the tree. Each node costs two D-Bus calls."""
|
|
||||||
if node is None or depth > max_depth:
|
|
||||||
return
|
|
||||||
role = safe(node.get_role)
|
|
||||||
states = None
|
|
||||||
if depth > 0:
|
|
||||||
states = safe(node.get_state_set)
|
|
||||||
if states is None or not states.contains(Atspi.StateType.SHOWING):
|
|
||||||
return
|
|
||||||
log(lambda: f"{' ' * depth}{safe(node.get_role_name, '?')} "
|
|
||||||
f"{(safe(node.get_name, '') or '')!r}")
|
|
||||||
yield node, role, states, ancestors, path
|
|
||||||
if role in PRUNE_ROLES:
|
|
||||||
return
|
|
||||||
for i, child in children(node):
|
|
||||||
yield from walk(child, ancestors + (role,), path + (i,), depth + 1,
|
|
||||||
max_depth)
|
|
||||||
|
|
||||||
|
|
||||||
def find_keepassxc():
|
|
||||||
desktop = Atspi.get_desktop(0)
|
|
||||||
for _, app in children(desktop):
|
|
||||||
if "keepassxc" in (safe(app.get_name, "") or "").lower():
|
|
||||||
return app
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def full_search(app):
|
|
||||||
"""Slow path: walk the tree. Returns (node, cache_record) or (None, None)."""
|
|
||||||
candidates = []
|
|
||||||
for node, role, states, ancestors, path in walk(app):
|
|
||||||
if not is_line_edit(role, states):
|
|
||||||
continue
|
|
||||||
name = safe(node.get_name, "") or ""
|
|
||||||
desc = safe(node.get_description, "") or ""
|
|
||||||
in_toolbar = Atspi.Role.TOOL_BAR in ancestors
|
|
||||||
log(lambda: f">> candidate: name={name!r} desc={desc!r} "
|
|
||||||
f"in_toolbar={in_toolbar} path={list(path)}")
|
|
||||||
record = {
|
|
||||||
"path": list(path),
|
|
||||||
"roles": [int(r) for r in ancestors] + [int(role)],
|
|
||||||
"name": name,
|
|
||||||
}
|
|
||||||
if "search" in f"{name} {desc}".lower():
|
|
||||||
return node, record
|
|
||||||
if in_toolbar:
|
|
||||||
candidates.append((node, record))
|
|
||||||
# The search field is the only editable line edit in KeePassXC's toolbar.
|
|
||||||
if len(candidates) == 1:
|
|
||||||
return candidates[0]
|
|
||||||
return None, None
|
|
||||||
|
|
||||||
|
|
||||||
def load_cache():
|
|
||||||
try:
|
|
||||||
return json.loads(CACHE_FILE.read_text())
|
|
||||||
except (OSError, ValueError):
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def save_cache(record):
|
|
||||||
try:
|
|
||||||
CACHE_FILE.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
|
|
||||||
CACHE_FILE.write_text(json.dumps(record))
|
|
||||||
except OSError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def cached_search(app, record):
|
|
||||||
"""Fast path: follow the remembered child indices, then verify the target.
|
|
||||||
Costs one D-Bus call per level plus a few for the check."""
|
|
||||||
if not record:
|
|
||||||
return None
|
|
||||||
path, roles, name = record.get("path"), record.get("roles"), record.get("name")
|
|
||||||
if not isinstance(path, list) or not isinstance(roles, list) \
|
|
||||||
or len(roles) != len(path) + 1:
|
|
||||||
return None
|
|
||||||
# Without a name to compare against, also check every level's role.
|
|
||||||
check_each_level = not name
|
|
||||||
node = app
|
|
||||||
for depth, i in enumerate(path):
|
|
||||||
node = child_at(node, i)
|
|
||||||
if node is None:
|
|
||||||
return None
|
|
||||||
if check_each_level and int(safe(node.get_role, -1)) != roles[depth + 1]:
|
|
||||||
return None
|
|
||||||
role = safe(node.get_role)
|
|
||||||
if role is None or int(role) != roles[-1]:
|
|
||||||
return None
|
|
||||||
if not is_line_edit(role, safe(node.get_state_set)):
|
|
||||||
return None
|
|
||||||
if (safe(node.get_name, "") or "") != name:
|
|
||||||
return None
|
|
||||||
return node
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
global DEBUG
|
|
||||||
ap = argparse.ArgumentParser()
|
|
||||||
ap.add_argument("query")
|
|
||||||
ap.add_argument("--timeout", type=float, default=0,
|
|
||||||
help="seconds to keep waiting for the search box "
|
|
||||||
"(e.g. while you unlock the database)")
|
|
||||||
ap.add_argument("--debug", action="store_true")
|
|
||||||
args = ap.parse_args()
|
|
||||||
DEBUG = args.debug
|
|
||||||
|
|
||||||
record = load_cache()
|
|
||||||
deadline = time.monotonic() + args.timeout
|
|
||||||
while True:
|
|
||||||
t0 = time.monotonic()
|
|
||||||
app = find_keepassxc()
|
|
||||||
box = None
|
|
||||||
if app is not None:
|
|
||||||
box = cached_search(app, record)
|
|
||||||
how = "cache"
|
|
||||||
if box is None:
|
|
||||||
how = "walk"
|
|
||||||
box, new_record = full_search(app)
|
|
||||||
if box is not None:
|
|
||||||
save_cache(new_record)
|
|
||||||
record = new_record
|
|
||||||
log(lambda: f"pass took {time.monotonic() - t0:.3f}s "
|
|
||||||
f"(app={app is not None}, box={box is not None}, via {how if app else '-'})")
|
|
||||||
if box is not None:
|
|
||||||
break
|
|
||||||
if time.monotonic() >= deadline:
|
|
||||||
if app is None:
|
|
||||||
print("KeePassXC not found on the accessibility bus",
|
|
||||||
file=sys.stderr)
|
|
||||||
return 1
|
|
||||||
print("search box not found (database locked?); "
|
|
||||||
"rerun with --debug to see the widget tree", file=sys.stderr)
|
|
||||||
return 2
|
|
||||||
time.sleep(0.2)
|
|
||||||
|
|
||||||
Atspi.EditableText.set_text_contents(box, args.query)
|
|
||||||
safe(lambda: Atspi.Component.grab_focus(box))
|
|
||||||
return 0
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
sys.exit(main())
|
|
||||||
|
|
@ -5,285 +5,282 @@
|
||||||
enable = true;
|
enable = true;
|
||||||
enableCompletion = false;
|
enableCompletion = false;
|
||||||
initExtra = ''
|
initExtra = ''
|
||||||
if [[ ! -v BASH_COMPLETION_VERSINFO ]] &&
|
if [[ ! -v BASH_COMPLETION_VERSINFO ]] &&
|
||||||
[[ -f "${pkgs.bash-completion}/etc/profile.d/bash_completion.sh" ]]; then
|
[[ -f "${pkgs.bash-completion}/etc/profile.d/bash_completion.sh" ]]; then
|
||||||
source "${pkgs.bash-completion}/etc/profile.d/bash_completion.sh"
|
source "${pkgs.bash-completion}/etc/profile.d/bash_completion.sh"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
[ "$DISPLAY" ] && xset r rate 300 60
|
[ "$DISPLAY" ] && xset r rate 300 60
|
||||||
|
|
||||||
#-##################### user specific aliases and functions ######################
|
#-##################### user specific aliases and functions ######################
|
||||||
if [ -d ~/.bashrc.d ]; then
|
if [ -d ~/.bashrc.d ]; then
|
||||||
for rc in ~/.bashrc.d/*; do
|
for rc in ~/.bashrc.d/*; do
|
||||||
bn=''${rc##*/}
|
bn=''${rc##*/}
|
||||||
if [ -f "''$rc" ] && [ -r "''$rc" ] && [ ! "''${bn::1}" = '.' ]; then
|
if [ -f "''$rc" ] && [ -r "''$rc" ] && [ ! "''${bn::1}" = '.' ]; then
|
||||||
\. "''$rc"
|
\. "''$rc"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
unset rc bn
|
unset rc bn
|
||||||
|
|
||||||
#-########################### shell settings & options ###########################
|
#-########################### shell settings & options ###########################
|
||||||
set -E
|
set -E
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
shopt -s nullglob dotglob globstar extglob
|
shopt -s nullglob dotglob globstar extglob
|
||||||
shopt -s autocd cdable_vars cdspell
|
shopt -s autocd cdable_vars cdspell
|
||||||
shopt -s checkwinsize
|
shopt -s checkwinsize
|
||||||
shopt -s lastpipe
|
shopt -s lastpipe
|
||||||
shopt -s inherit_errexit
|
shopt -s inherit_errexit
|
||||||
shopt -s no_empty_cmd_completion
|
shopt -s no_empty_cmd_completion
|
||||||
shopt -s histreedit
|
shopt -s histreedit
|
||||||
shopt -s lithist
|
shopt -s lithist
|
||||||
shopt -u histverify
|
shopt -u histverify
|
||||||
_have bash_abbr && shopt -u expand_aliases
|
_have bash_abbr && shopt -u expand_aliases
|
||||||
|
|
||||||
#-############################### shell variables ################################
|
#-############################### shell variables ################################
|
||||||
FIGNORE='.o'
|
FIGNORE='.o'
|
||||||
# Warning
|
# Warning
|
||||||
HISTCONTROL=ignoreboth:erasedups
|
HISTCONTROL=ignoreboth:erasedups
|
||||||
HISTTIMEFORMAT='%y-%m-%d %H:%M:%S '
|
HISTTIMEFORMAT='%y-%m-%d %H:%M:%S '
|
||||||
HISTSIZE=-1
|
HISTSIZE=-1
|
||||||
HISTFILESIZE=-1
|
HISTFILESIZE=-1
|
||||||
HISTFILE=''$HOME/.''${USER:-default}_bash_history
|
HISTFILE=''$HOME/.''${USER:-default}_bash_history
|
||||||
|
|
||||||
#-############################ environment variables #############################
|
#-############################ environment variables #############################
|
||||||
export PYTHON_BASIC_REPL=1 # I'm good at readline so I'm gonna use readline whereever possible!
|
export PYTHON_BASIC_REPL=1 # I'm good at readline so I'm gonna use readline whereever possible!
|
||||||
export LESS='-SRFX'
|
export LESS='-SRFX'
|
||||||
export PAGER='less' GIT_PAGER='less' MANPAGER='less' SYSTEMD_PAGER='less'
|
export PAGER='less' GIT_PAGER='less' MANPAGER='less' SYSTEMD_PAGER='less'
|
||||||
export BAT_PAGER='''
|
export BAT_PAGER='''
|
||||||
export EDITOR='vim' VISUAL='vim' FCEDIT='vim' SYSTEMD_EDITOR='vim'
|
export EDITOR='vim' VISUAL='vim' FCEDIT='vim' SYSTEMD_EDITOR='vim'
|
||||||
export PATH
|
export PATH
|
||||||
|
|
||||||
#-########################## generally useful functions ##########################
|
#-########################## generally useful functions ##########################
|
||||||
take () {
|
take () {
|
||||||
: 'Idempotently create directory recursively and cd into it'
|
: 'Idempotently create directory recursively and cd into it'
|
||||||
local out
|
local out
|
||||||
out=''$(set -o pipefail && mkdir -vp "''$@" | tail -1) || return
|
out=''$(set -o pipefail && mkdir -vp "''$@" | tail -1) || return
|
||||||
printf '%s\n' "''$out"
|
printf '%s\n' "''$out"
|
||||||
eval "out=''${out#"mkdir: created directory "}"
|
eval "out=''${out#"mkdir: created directory "}"
|
||||||
CDPATH= cd "''${out:-''${@: -1}}"
|
CDPATH= cd "''${out:-''${@: -1}}"
|
||||||
}
|
}
|
||||||
|
|
||||||
safe_ls () {
|
safe_ls () {
|
||||||
command ls --color=always --quoting-style=shell-escape -lArth 2>/dev/null || \
|
command ls --color=always --quoting-style=shell-escape -lArth 2>/dev/null || \
|
||||||
command ls --color=always -q -lArth 2>/dev/null || \
|
command ls --color=always -q -lArth 2>/dev/null || \
|
||||||
command ls --color=always -lArth 2>/dev/null || \
|
command ls --color=always -lArth 2>/dev/null || \
|
||||||
command ls --color -lArth 2>/dev/null || \
|
command ls --color -lArth 2>/dev/null || \
|
||||||
command ls -lArth 2>/dev/null || \
|
command ls -lArth 2>/dev/null || \
|
||||||
command ls -lArt 2>/dev/null || \
|
command ls -lArt 2>/dev/null || \
|
||||||
command ls -lAr 2>/dev/null || \
|
command ls -lAr 2>/dev/null || \
|
||||||
command ls -lar 2>/dev/null || \
|
command ls -lar 2>/dev/null || \
|
||||||
command ls -la 2>/dev/null || \
|
command ls -la 2>/dev/null || \
|
||||||
command ls -l 2>/dev/null || \
|
command ls -l 2>/dev/null || \
|
||||||
command ls -1 2>/dev/null || \
|
command ls -1 2>/dev/null || \
|
||||||
command printf '%q\n' * 2>/dev/null || \
|
command printf '%q\n' * 2>/dev/null || \
|
||||||
command printf '%s\n' * 2>/dev/null
|
command printf '%s\n' * 2>/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
cd () {
|
cd () {
|
||||||
: 'Run cd; on success in a terminal, show the 4 newest entries (with a count of hidden ones)'
|
: 'Run cd; on success in a terminal, show the 4 newest entries (with a count of hidden ones)'
|
||||||
|
|
||||||
builtin cd "''$@" || return
|
builtin cd "''$@" || return
|
||||||
[[ -t 1 ]] || return 0
|
[[ -t 1 ]] || return 0
|
||||||
command -v ls &>/dev/null || return 0
|
command -v ls &>/dev/null || return 0
|
||||||
|
|
||||||
local -a lines
|
local -a lines
|
||||||
mapfile -t lines < <(safe_ls)
|
mapfile -t lines < <(safe_ls)
|
||||||
if (( ''${#lines[@]} > 10 )); then
|
if (( ''${#lines[@]} > 10 )); then
|
||||||
printf '\033[7m... %d more entries ...\033[27m\n' "$(( ''${#lines[@]} - 10 ))";
|
printf '\033[7m... %d more entries ...\033[27m\n' "$(( ''${#lines[@]} - 10 ))";
|
||||||
printf '%s\n' "''${lines[@]: -9}"
|
printf '%s\n' "''${lines[@]: -9}"
|
||||||
else
|
else
|
||||||
printf '%s\n' "''${lines[@]}"
|
printf '%s\n' "''${lines[@]}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
drop_caches () {
|
drop_caches () {
|
||||||
: 'Drop pagecache, dentries and inodes'
|
: 'Drop pagecache, dentries and inodes'
|
||||||
: 'https://unix.stackexchange.com/a/87909'
|
: 'https://unix.stackexchange.com/a/87909'
|
||||||
echo 3 | sudo tee /proc/sys/vm/drop_caches >/dev/null
|
echo 3 | sudo tee /proc/sys/vm/drop_caches >/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
respawn_shell () {
|
respawn_shell () {
|
||||||
: 'Intelligently respawn shell'
|
: 'Intelligently respawn shell'
|
||||||
: 'pure posix sh without forking'
|
: 'pure posix sh without forking'
|
||||||
if [ "''${BASH_VERSINFO-}" ] && command -v bash 1>/dev/null 2>&1; then
|
if [ "''${BASH_VERSINFO-}" ] && command -v bash 1>/dev/null 2>&1; then
|
||||||
if _is_login_shell; then
|
if _is_login_shell; then
|
||||||
exec bash -l
|
exec bash -l
|
||||||
else
|
else
|
||||||
exec bash
|
exec bash
|
||||||
fi
|
fi
|
||||||
: 'TODO: Implement more detection mechanisms for various shells (zsh, etc.)'
|
: 'TODO: Implement more detection mechanisms for various shells (zsh, etc.)'
|
||||||
elif [ -x /proc/self/exe ]; then
|
elif [ -x /proc/self/exe ]; then
|
||||||
if _is_login_shell; then
|
if _is_login_shell; then
|
||||||
exec -l /proc/self/exe
|
exec -l /proc/self/exe
|
||||||
else
|
else
|
||||||
exec /proc/self/exe
|
exec /proc/self/exe
|
||||||
fi
|
fi
|
||||||
elif command -v "''${0#-}" 1>/dev/null 2>&1; then
|
elif command -v "''${0#-}" 1>/dev/null 2>&1; then
|
||||||
: 'TODO: Make it work with shells that actually start with a dash (unlikely, but possible)'
|
: 'TODO: Make it work with shells that actually start with a dash (unlikely, but possible)'
|
||||||
printf 'Warning: replacing current shell with unreliable ''$0, which is: %s\n' "''$0"
|
printf 'Warning: replacing current shell with unreliable ''$0, which is: %s\n' "''$0"
|
||||||
if _is_login_shell; then
|
if _is_login_shell; then
|
||||||
exec -l "''${0#-}"
|
exec -l "''${0#-}"
|
||||||
else
|
else
|
||||||
exec "''${0#-}"
|
exec "''${0#-}"
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
printf 'Error: Don'\'''t know how to reliably replace this shell, not exec'\'''ing and not exiting\n'
|
printf 'Error: Don'\'''t know how to reliably replace this shell, not exec'\'''ing and not exiting\n'
|
||||||
printf 'You might want to manually run `exec [-l] "''$SHELL"`, however, `''$SHELL` might not be the shell you are currently running'
|
printf 'You might want to manually run `exec [-l] "''$SHELL"`, however, `''$SHELL` might not be the shell you are currently running'
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
export_bashrc () {
|
export_bashrc () {
|
||||||
CDPATH= command cd ~/.bashrc.d/ || return
|
CDPATH= command cd ~/.bashrc.d/ || return
|
||||||
: 'Not using an associative array because those are not sorted!'
|
: 'Not using an associative array because those are not sorted!'
|
||||||
declare -ar plugins=(*)
|
declare -ar plugins=(*)
|
||||||
[[ ''${#plugins[@]} -eq 0 ]] || [[ ''${#plugins} -eq 1 && ''${plugins[0]} = '*' ]] && return
|
[[ ''${#plugins[@]} -eq 0 ]] || [[ ''${#plugins} -eq 1 && ''${plugins[0]} = '*' ]] && return
|
||||||
local IFS=' '
|
local IFS=' '
|
||||||
declare -Ai selected
|
declare -Ai selected
|
||||||
declare plugin
|
declare plugin
|
||||||
for plugin in "''${plugins[@]}"; do
|
for plugin in "''${plugins[@]}"; do
|
||||||
case "''$plugin" in
|
case "''$plugin" in
|
||||||
#-# Comment below case match if you don'\'''t want to have a pre-selection of plugins to export
|
#-# Comment below case match if you don'\'''t want to have a pre-selection of plugins to export
|
||||||
#-# Requires `shopt -s extglob`
|
#-# Requires `shopt -s extglob`
|
||||||
(+([0-9])-@(util|redact|prompt|docker-tools|funcs|init-and-complete|general-aliases|bash-abbr)) selected[''$plugin]=1 ;;
|
(+([0-9])-@(util|redact|prompt|docker-tools|funcs|init-and-complete|general-aliases|bash-abbr)) selected[''$plugin]=1 ;;
|
||||||
(*) selected[''$plugin]=
|
(*) selected[''$plugin]=
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
declare -a plugins_with_selection
|
declare -a plugins_with_selection
|
||||||
while :; do
|
while :; do
|
||||||
local PS3='Which plugins do you want to export/not export (enter a single number)? '
|
local PS3='Which plugins do you want to export/not export (enter a single number)? '
|
||||||
plugins_with_selection=()
|
plugins_with_selection=()
|
||||||
for plugin in "''${plugins[@]}"; do
|
for plugin in "''${plugins[@]}"; do
|
||||||
if [[ ''${selected[''$plugin]} -eq 0 ]]; then
|
if [[ ''${selected[''$plugin]} -eq 0 ]]; then
|
||||||
plugins_with_selection+=(''$'\033[31m'''$plugin''$'\033[m')
|
plugins_with_selection+=(''$'\033[31m'''$plugin''$'\033[m')
|
||||||
else
|
else
|
||||||
plugins_with_selection+=(''$'\033[32m'''$plugin''$'\033[m')
|
plugins_with_selection+=(''$'\033[32m'''$plugin''$'\033[m')
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
select plugin in ''$'\033[31mQUIT\033[m' ''$'\033[34mDONE\033[m' "''${plugins_with_selection[@]}"; do
|
select plugin in ''$'\033[31mQUIT\033[m' ''$'\033[34mDONE\033[m' "''${plugins_with_selection[@]}"; do
|
||||||
plugin=''${plugin#''$'\033[3'[0-7]m}
|
plugin=''${plugin#''$'\033[3'[0-7]m}
|
||||||
plugin=''${plugin%''$'\033[m'}
|
plugin=''${plugin%''$'\033[m'}
|
||||||
[[ -z ''$REPLY || ''$plugin = QUIT || ''$plugin = DONE ]] && break 2
|
[[ -z ''$REPLY || ''$plugin = QUIT || ''$plugin = DONE ]] && break 2
|
||||||
[[ -z ''$plugin ]] && { echo "Please enter a number (1-''$((''${#plugins[@]}+2)))."; continue; }
|
[[ -z ''$plugin ]] && { echo "Please enter a number (1-''$((''${#plugins[@]}+2)))."; continue; }
|
||||||
selected[''$plugin]=1-''${selected[''$plugin]}
|
selected[''$plugin]=1-''${selected[''$plugin]}
|
||||||
break
|
break
|
||||||
done
|
done
|
||||||
[[ -z ''$REPLY || ''$plugin = QUIT || ''$plugin = DONE ]] && break
|
[[ -z ''$REPLY || ''$plugin = QUIT || ''$plugin = DONE ]] && break
|
||||||
done
|
done
|
||||||
[[ -z ''$REPLY || ''$plugin = QUIT ]] && { echo "Aborting"; return; }
|
[[ -z ''$REPLY || ''$plugin = QUIT ]] && { echo "Aborting"; return; }
|
||||||
declare -a exported_plugins=()
|
declare -a exported_plugins=()
|
||||||
for plugin in "''${!selected[@]}"; do
|
for plugin in "''${!selected[@]}"; do
|
||||||
if [[ ''${selected[''$plugin]} -eq 1 ]]; then
|
if [[ ''${selected[''$plugin]} -eq 1 ]]; then
|
||||||
exported_plugins+=(".bashrc.d/''$plugin")
|
exported_plugins+=(".bashrc.d/''$plugin")
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
command rm -vf "exported_bashrc.tar.gz"
|
command rm -vf "exported_bashrc.tar.gz"
|
||||||
CDPATH= command cd - >/dev/null
|
CDPATH= command cd - >/dev/null
|
||||||
CDPATH= command cd ~
|
CDPATH= command cd ~
|
||||||
as_base64=''$(command tar -cvhzf - ".bashrc" "''${exported_plugins[@]}" | base64 -w0)
|
as_base64=''$(command tar -cvhzf - ".bashrc" "''${exported_plugins[@]}" | base64 -w0)
|
||||||
CDPATH= command cd - >/dev/null
|
CDPATH= command cd - >/dev/null
|
||||||
as_base64_wrapped=''$(printf %s "''$as_base64" | sed 's#.\{,70\}#'\'''&'\'''\\\n#g' | sed ''\'''$s#\\$##')
|
as_base64_wrapped=''$(printf %s "''$as_base64" | sed 's#.\{,70\}#'\'''&'\'''\\\n#g' | sed ''\'''$s#\\$##')
|
||||||
command install -m775 /dev/stdin ~/bashrc_installer.sh <<-EOF
|
command install -m775 /dev/stdin ~/bashrc_installer.sh <<-EOF
|
||||||
#! /usr/bin/env bash
|
#! /usr/bin/env bash
|
||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
[ "\''$BASH_VERSINFO" ] && command -v declare >/dev/null 2>&1 && command declare -p BASH_VERSINFO | { IFS=' ' read -r _ flags _; case "\''$flags" in (*r*) ;; (*) false; esac; } || { printf 'This installer must be run with bash!\\n'; exit 1; }
|
[ "\''$BASH_VERSINFO" ] && command -v declare >/dev/null 2>&1 && command declare -p BASH_VERSINFO | { IFS=' ' read -r _ flags _; case "\''$flags" in (*r*) ;; (*) false; esac; } || { printf 'This installer must be run with bash!\\n'; exit 1; }
|
||||||
command -v bash >/dev/null 2>&1 || { printf 'bash must be installed!\\n'; exit 2; }
|
command -v bash >/dev/null 2>&1 || { printf 'bash must be installed!\\n'; exit 2; }
|
||||||
|
|
||||||
as_base64=''$as_base64_wrapped
|
as_base64=''$as_base64_wrapped
|
||||||
tempdir=\''$(mktemp -d)
|
tempdir=\''$(mktemp -d)
|
||||||
trap 'rm -rf -- "\''$tempdir"' EXIT
|
trap 'rm -rf -- "\''$tempdir"' EXIT
|
||||||
cd -- "\''$tempdir"
|
cd -- "\''$tempdir"
|
||||||
base64 -d <<< "\''$as_base64" | tar xzf -
|
base64 -d <<< "\''$as_base64" | tar xzf -
|
||||||
create_dir_and_move () {
|
create_dir_and_move () {
|
||||||
src=\''$1
|
src=\''$1
|
||||||
dst=\''$2
|
dst=\''$2
|
||||||
[ ! -f "\''$src" ] && { printf '%s\\n' "Source argument '\''$src' is not a regular file"; return 1; }
|
[ ! -f "\''$src" ] && { printf '%s\\n' "Source argument '\''$src' is not a regular file"; return 1; }
|
||||||
dst_dir=\''$(dirname -- "\''${dst//[^!-~]/-}")
|
dst_dir=\''$(dirname -- "\''${dst//[^!-~]/-}")
|
||||||
[ ! -d "\''$dst_dir" ] && { printf '%s\\n' "Trying to create destination directory '\''$dst_dir'"; mkdir -p -- "\''$dst_dir"; } || :
|
[ ! -d "\''$dst_dir" ] && { printf '%s\\n' "Trying to create destination directory '\''$dst_dir'"; mkdir -p -- "\''$dst_dir"; } || :
|
||||||
[ -d "\''$dst_dir" ] && mv -v -t "\''$dst_dir" -- "\''$src" || :
|
[ -d "\''$dst_dir" ] && mv -v -t "\''$dst_dir" -- "\''$src" || :
|
||||||
}
|
}
|
||||||
ask_overwrite () {
|
ask_overwrite () {
|
||||||
file1=\''$1
|
file1=\''$1
|
||||||
file2=\''$2
|
file2=\''$2
|
||||||
if [[ ! -e \''$file2 ]]; then
|
if [[ ! -e \''$file2 ]]; then
|
||||||
create_dir_and_move "\''$file1" "\''$file2"
|
create_dir_and_move "\''$file1" "\''$file2"
|
||||||
elif [[ -d \''$file2 ]]; then
|
elif [[ -d \''$file2 ]]; then
|
||||||
printf 'WARNING: Destination file %s is a directory! Skipping this file\\n' "\''${file2@Q}"
|
printf 'WARNING: Destination file %s is a directory! Skipping this file\\n' "\''${file2@Q}"
|
||||||
return
|
return
|
||||||
elif [[ ! -f \''$file2 ]]; then
|
elif [[ ! -f \''$file2 ]]; then
|
||||||
printf 'WARNING: Destination file %s is not a regular file! Skipping this file\\n' "\''${file2@Q}"
|
printf 'WARNING: Destination file %s is not a regular file! Skipping this file\\n' "\''${file2@Q}"
|
||||||
return
|
return
|
||||||
elif ! diff -q "\''$file1" "\''$file2"; then
|
elif ! diff -q "\''$file1" "\''$file2"; then
|
||||||
printf 'File to be installed (%s) differs from already installed file (%s)\\n' "\''${file1@Q}" "\''${file2@Q}"
|
printf 'File to be installed (%s) differs from already installed file (%s)\\n' "\''${file1@Q}" "\''${file2@Q}"
|
||||||
read -p 'Press enter to show diff. ' || :
|
read -p 'Press enter to show diff. ' || :
|
||||||
diff --color -u "\''$file1" "\''$file2" || :
|
diff --color -u "\''$file1" "\''$file2" || :
|
||||||
printf 'Giving you a plain bash shell to resolve the situation. Once done, just exit.\\n'
|
printf 'Giving you a plain bash shell to resolve the situation. Once done, just exit.\\n'
|
||||||
printf 'After you exit, the destination file WILL BE FORCEFULLY OVERWRITTEN!!!\n'
|
printf 'After you exit, the destination file WILL BE FORCEFULLY OVERWRITTEN!!!\n'
|
||||||
read -p 'Press enter to continue in the shell. ' || :
|
read -p 'Press enter to continue in the shell. ' || :
|
||||||
bash --rcfile <(printf 'cd; printf "You are running bash version \''$BASH_VERSION. Your CWD is \''$PWD. Your HOME is \''$HOME. You are \''$USER and HOSTNAME is \''$HOSTNAME.\\nUse this bash shell to make changes, backups, etc.\\nThen just exit.\\n"') --noprofile || :
|
bash --rcfile <(printf 'cd; printf "You are running bash version \''$BASH_VERSION. Your CWD is \''$PWD. Your HOME is \''$HOME. You are \''$USER and HOSTNAME is \''$HOSTNAME.\\nUse this bash shell to make changes, backups, etc.\\nThen just exit.\\n"') --noprofile || :
|
||||||
printf 'Destination file %s will be overwritten. Continue? ' "\''${file2@Q}"
|
printf 'Destination file %s will be overwritten. Continue? ' "\''${file2@Q}"
|
||||||
read || :
|
read || :
|
||||||
create_dir_and_move "\''$file1" "\''$file2"
|
create_dir_and_move "\''$file1" "\''$file2"
|
||||||
else
|
else
|
||||||
printf 'File %s was not changed\\n' "\''${file2@Q}"
|
printf 'File %s was not changed\\n' "\''${file2@Q}"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
ask_overwrite .bashrc ~/.bashrc
|
ask_overwrite .bashrc ~/.bashrc
|
||||||
set +f
|
set +f
|
||||||
shopt -s nullglob dotglob
|
shopt -s nullglob dotglob
|
||||||
for file in .bashrc.d/*; do
|
for file in .bashrc.d/*; do
|
||||||
ask_overwrite "\''$file" ~/"\''$file"
|
ask_overwrite "\''$file" ~/"\''$file"
|
||||||
done
|
done
|
||||||
cd - >/dev/null
|
cd - >/dev/null
|
||||||
read -p 'Self destruct (y/N)? ' choice || :
|
read -p 'Self destruct (y/N)? ' choice || :
|
||||||
[[ \''$choice = y ]] && { printf 'Trying to rm myself (%s)\\n' "\''${0@Q}"; rm -- "\''$0" && printf 'Success\\n' || printf 'Failed. Not trying further\\n'; }
|
[[ \''$choice = y ]] && { printf 'Trying to rm myself (%s)\\n' "\''${0@Q}"; rm -- "\''$0" && printf 'Success\\n' || printf 'Failed. Not trying further\\n'; }
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
#-#################################### binds #####################################
|
#-#################################### binds #####################################
|
||||||
bind '"\e\C-r": kill-region' \
|
bind '"\e\C-r": kill-region' \
|
||||||
'"\C-x\C-k": kill-whole-line' \
|
'"\C-x\C-k": kill-whole-line' \
|
||||||
'"\e\C-p": history-search-backward' \
|
'"\e\C-p": history-search-backward' \
|
||||||
'"\e\C-n": history-search-forward' \
|
'"\e\C-n": history-search-forward' \
|
||||||
'"\eP": history-substring-search-backward' \
|
'"\eP": history-substring-search-backward' \
|
||||||
'"\eN": history-substring-search-forward' \
|
'"\eN": history-substring-search-forward' \
|
||||||
'"\C-x\C-d": "\e#\C-m exit\C-m"' \
|
'"\C-x\C-d": "\e#\C-m exit\C-m"' \
|
||||||
'"\ej": "\C-e |& tac | \\cat -n | tac | \\cat -n | less -SFXR\C-m"' \
|
'"\ej": "\C-e |& tac | \\cat -n | tac | \\cat -n | less -SFXR\C-m"' \
|
||||||
'"\eX": ">/dev/null "' \
|
'"\eX": ">/dev/null "' \
|
||||||
'"\t": menu-complete' \
|
'"\t": menu-complete' \
|
||||||
'"\e[Z": menu-complete-backward' \
|
'"\e[Z": menu-complete-backward' \
|
||||||
'"\C-x\C-r": forward-search-history' \
|
'"\C-x\C-r": forward-search-history' \
|
||||||
'set show-all-if-ambiguous on' \
|
'set show-all-if-ambiguous on' \
|
||||||
'set completion-query-items 0' \
|
'set completion-query-items 0' \
|
||||||
'set menu-complete-display-prefix on' \
|
'set menu-complete-display-prefix on' \
|
||||||
'set colored-completion-prefix on' \
|
'set colored-completion-prefix on' \
|
||||||
'set colored-stats on' \
|
'set colored-stats on' \
|
||||||
'set mark-modified-lines on' \
|
'set history-preserve-point on'
|
||||||
'set revert-all-at-newline on' \
|
|
||||||
'set blink-matching-paren on' \
|
|
||||||
'set history-preserve-point on'
|
|
||||||
|
|
||||||
#-################################## autoclear ###################################
|
#-################################## autoclear ###################################
|
||||||
#-# TODO
|
#-# TODO
|
||||||
|
|
||||||
#-# prank () {
|
#-# prank () {
|
||||||
#-# line_up_until_cursor=''${READLINE_LINE::READLINE_POINT}
|
#-# line_up_until_cursor=''${READLINE_LINE::READLINE_POINT}
|
||||||
#-# line_after_cursor=''${READLINE_LINE:READLINE_POINT}
|
#-# line_after_cursor=''${READLINE_LINE:READLINE_POINT}
|
||||||
#-# if [[ ''$((RANDOM % 5)) -eq 0 ]]; then
|
#-# if [[ ''$((RANDOM % 5)) -eq 0 ]]; then
|
||||||
#-# READLINE_LINE="''$line_up_until_cursor''$line_after_cursor"
|
#-# READLINE_LINE="''$line_up_until_cursor''$line_after_cursor"
|
||||||
#-# #READLINE_LINE="''$line_up_until_cursor''${1^}''$line_after_cursor"
|
#-# #READLINE_LINE="''$line_up_until_cursor''${1^}''$line_after_cursor"
|
||||||
#-# READLINE_POINT=''$((READLINE_POINT+1))
|
#-# READLINE_POINT=''$((READLINE_POINT+1))
|
||||||
#-# else
|
#-# else
|
||||||
#-# READLINE_LINE="''$line_up_until_cursor''$1''$line_after_cursor"
|
#-# READLINE_LINE="''$line_up_until_cursor''$1''$line_after_cursor"
|
||||||
#-# READLINE_POINT=''$((READLINE_POINT+1))
|
#-# READLINE_POINT=''$((READLINE_POINT+1))
|
||||||
#-# fi
|
#-# fi
|
||||||
#-# }
|
#-# }
|
||||||
#-# bind -x '" ": prank " "'
|
#-# bind -x '" ": prank " "'
|
||||||
#-# bind -x '"a": prank "a"'
|
#-# bind -x '"a": prank "a"'
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue