diff --git a/hosts/fw/configuration.nix b/hosts/fw/configuration.nix index 03d7e77..56d6ff1 100644 --- a/hosts/fw/configuration.nix +++ b/hosts/fw/configuration.nix @@ -406,7 +406,6 @@ with lib; gnupg gron guestfs-tools - gurk-rs htop ijq iperf3 @@ -425,7 +424,6 @@ with lib; mosh mpv mupdf - nautilus neomutt netbird-ui netcat-openbsd @@ -439,7 +437,6 @@ with lib; openvpn pangolin-cli paprefs - parted patchelf pavucontrol pciutils @@ -455,6 +452,7 @@ with lib; signal-cli signal-cli signal-desktop + gurk-rs simplex-chat-desktop slack spacer @@ -482,13 +480,8 @@ with lib; yq yt-dlp yubioath-flutter - - (pkgs.writeShellScriptBin "kpxc-search" '' - 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} "$@" - '') + nautilus + parted ]; programs.ydotool.enable = true; diff --git a/hosts/fw/scripts/kpxc-search b/hosts/fw/scripts/kpxc-search deleted file mode 100755 index 0ac7d63..0000000 --- a/hosts/fw/scripts/kpxc-search +++ /dev/null @@ -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()) diff --git a/modules/home-manager/bash.nix b/modules/home-manager/bash.nix index 6dc6cfc..0443b88 100644 --- a/modules/home-manager/bash.nix +++ b/modules/home-manager/bash.nix @@ -5,285 +5,282 @@ enable = true; enableCompletion = false; initExtra = '' - if [[ ! -v BASH_COMPLETION_VERSINFO ]] && - [[ -f "${pkgs.bash-completion}/etc/profile.d/bash_completion.sh" ]]; then - source "${pkgs.bash-completion}/etc/profile.d/bash_completion.sh" - fi +if [[ ! -v BASH_COMPLETION_VERSINFO ]] && + [[ -f "${pkgs.bash-completion}/etc/profile.d/bash_completion.sh" ]]; then + source "${pkgs.bash-completion}/etc/profile.d/bash_completion.sh" +fi - [ "$DISPLAY" ] && xset r rate 300 60 +[ "$DISPLAY" ] && xset r rate 300 60 - #-##################### user specific aliases and functions ###################### - if [ -d ~/.bashrc.d ]; then - for rc in ~/.bashrc.d/*; do - bn=''${rc##*/} - if [ -f "''$rc" ] && [ -r "''$rc" ] && [ ! "''${bn::1}" = '.' ]; then - \. "''$rc" - fi - done - fi - unset rc bn +#-##################### user specific aliases and functions ###################### +if [ -d ~/.bashrc.d ]; then + for rc in ~/.bashrc.d/*; do + bn=''${rc##*/} + if [ -f "''$rc" ] && [ -r "''$rc" ] && [ ! "''${bn::1}" = '.' ]; then + \. "''$rc" + fi + done +fi +unset rc bn - #-########################### shell settings & options ########################### - set -E - set -o pipefail +#-########################### shell settings & options ########################### +set -E +set -o pipefail - shopt -s nullglob dotglob globstar extglob - shopt -s autocd cdable_vars cdspell - shopt -s checkwinsize - shopt -s lastpipe - shopt -s inherit_errexit - shopt -s no_empty_cmd_completion - shopt -s histreedit - shopt -s lithist - shopt -u histverify - _have bash_abbr && shopt -u expand_aliases +shopt -s nullglob dotglob globstar extglob +shopt -s autocd cdable_vars cdspell +shopt -s checkwinsize +shopt -s lastpipe +shopt -s inherit_errexit +shopt -s no_empty_cmd_completion +shopt -s histreedit +shopt -s lithist +shopt -u histverify +_have bash_abbr && shopt -u expand_aliases - #-############################### shell variables ################################ - FIGNORE='.o' - # Warning - HISTCONTROL=ignoreboth:erasedups - HISTTIMEFORMAT='%y-%m-%d %H:%M:%S ' - HISTSIZE=-1 - HISTFILESIZE=-1 - HISTFILE=''$HOME/.''${USER:-default}_bash_history +#-############################### shell variables ################################ +FIGNORE='.o' +# Warning +HISTCONTROL=ignoreboth:erasedups +HISTTIMEFORMAT='%y-%m-%d %H:%M:%S ' +HISTSIZE=-1 +HISTFILESIZE=-1 +HISTFILE=''$HOME/.''${USER:-default}_bash_history - #-############################ environment variables ############################# - export PYTHON_BASIC_REPL=1 # I'm good at readline so I'm gonna use readline whereever possible! - export LESS='-SRFX' - export PAGER='less' GIT_PAGER='less' MANPAGER='less' SYSTEMD_PAGER='less' - export BAT_PAGER=''' - export EDITOR='vim' VISUAL='vim' FCEDIT='vim' SYSTEMD_EDITOR='vim' - export PATH +#-############################ environment variables ############################# +export PYTHON_BASIC_REPL=1 # I'm good at readline so I'm gonna use readline whereever possible! +export LESS='-SRFX' +export PAGER='less' GIT_PAGER='less' MANPAGER='less' SYSTEMD_PAGER='less' +export BAT_PAGER=''' +export EDITOR='vim' VISUAL='vim' FCEDIT='vim' SYSTEMD_EDITOR='vim' +export PATH - #-########################## generally useful functions ########################## - take () { - : 'Idempotently create directory recursively and cd into it' - local out - out=''$(set -o pipefail && mkdir -vp "''$@" | tail -1) || return - printf '%s\n' "''$out" - eval "out=''${out#"mkdir: created directory "}" - CDPATH= cd "''${out:-''${@: -1}}" - } +#-########################## generally useful functions ########################## +take () { + : 'Idempotently create directory recursively and cd into it' + local out + out=''$(set -o pipefail && mkdir -vp "''$@" | tail -1) || return + printf '%s\n' "''$out" + eval "out=''${out#"mkdir: created directory "}" + CDPATH= cd "''${out:-''${@: -1}}" +} - safe_ls () { - 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 -lArth 2>/dev/null || \ - command ls --color -lArth 2>/dev/null || \ - command ls -lArth 2>/dev/null || \ - command ls -lArt 2>/dev/null || \ - command ls -lAr 2>/dev/null || \ - command ls -lar 2>/dev/null || \ - command ls -la 2>/dev/null || \ - command ls -l 2>/dev/null || \ - command ls -1 2>/dev/null || \ - command printf '%q\n' * 2>/dev/null || \ - command printf '%s\n' * 2>/dev/null - } +safe_ls () { + 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 -lArth 2>/dev/null || \ + command ls --color -lArth 2>/dev/null || \ + command ls -lArth 2>/dev/null || \ + command ls -lArt 2>/dev/null || \ + command ls -lAr 2>/dev/null || \ + command ls -lar 2>/dev/null || \ + command ls -la 2>/dev/null || \ + command ls -l 2>/dev/null || \ + command ls -1 2>/dev/null || \ + command printf '%q\n' * 2>/dev/null || \ + command printf '%s\n' * 2>/dev/null +} - cd () { - : 'Run cd; on success in a terminal, show the 4 newest entries (with a count of hidden ones)' +cd () { + : 'Run cd; on success in a terminal, show the 4 newest entries (with a count of hidden ones)' - builtin cd "''$@" || return - [[ -t 1 ]] || return 0 - command -v ls &>/dev/null || return 0 + builtin cd "''$@" || return + [[ -t 1 ]] || return 0 + command -v ls &>/dev/null || return 0 - local -a lines - mapfile -t lines < <(safe_ls) - if (( ''${#lines[@]} > 10 )); then - printf '\033[7m... %d more entries ...\033[27m\n' "$(( ''${#lines[@]} - 10 ))"; - printf '%s\n' "''${lines[@]: -9}" - else - printf '%s\n' "''${lines[@]}" - fi - } + local -a lines + mapfile -t lines < <(safe_ls) + if (( ''${#lines[@]} > 10 )); then + printf '\033[7m... %d more entries ...\033[27m\n' "$(( ''${#lines[@]} - 10 ))"; + printf '%s\n' "''${lines[@]: -9}" + else + printf '%s\n' "''${lines[@]}" + fi +} - drop_caches () { - : 'Drop pagecache, dentries and inodes' - : 'https://unix.stackexchange.com/a/87909' - echo 3 | sudo tee /proc/sys/vm/drop_caches >/dev/null - } +drop_caches () { + : 'Drop pagecache, dentries and inodes' + : 'https://unix.stackexchange.com/a/87909' + echo 3 | sudo tee /proc/sys/vm/drop_caches >/dev/null +} - respawn_shell () { - : 'Intelligently respawn shell' - : 'pure posix sh without forking' - if [ "''${BASH_VERSINFO-}" ] && command -v bash 1>/dev/null 2>&1; then - if _is_login_shell; then - exec bash -l - else - exec bash - fi - : 'TODO: Implement more detection mechanisms for various shells (zsh, etc.)' - elif [ -x /proc/self/exe ]; then - if _is_login_shell; then - exec -l /proc/self/exe - else - exec /proc/self/exe - fi - 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)' - printf 'Warning: replacing current shell with unreliable ''$0, which is: %s\n' "''$0" - if _is_login_shell; then - exec -l "''${0#-}" - else - exec "''${0#-}" - fi - else - 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' - return 1 - fi - } +respawn_shell () { + : 'Intelligently respawn shell' + : 'pure posix sh without forking' + if [ "''${BASH_VERSINFO-}" ] && command -v bash 1>/dev/null 2>&1; then + if _is_login_shell; then + exec bash -l + else + exec bash + fi + : 'TODO: Implement more detection mechanisms for various shells (zsh, etc.)' + elif [ -x /proc/self/exe ]; then + if _is_login_shell; then + exec -l /proc/self/exe + else + exec /proc/self/exe + fi + 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)' + printf 'Warning: replacing current shell with unreliable ''$0, which is: %s\n' "''$0" + if _is_login_shell; then + exec -l "''${0#-}" + else + exec "''${0#-}" + fi + else + 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' + return 1 + fi +} - export_bashrc () { - CDPATH= command cd ~/.bashrc.d/ || return - : 'Not using an associative array because those are not sorted!' - declare -ar plugins=(*) - [[ ''${#plugins[@]} -eq 0 ]] || [[ ''${#plugins} -eq 1 && ''${plugins[0]} = '*' ]] && return - local IFS=' ' - declare -Ai selected - declare plugin - for plugin in "''${plugins[@]}"; do - case "''$plugin" in - #-# Comment below case match if you don'\'''t want to have a pre-selection of plugins to export - #-# Requires `shopt -s extglob` - (+([0-9])-@(util|redact|prompt|docker-tools|funcs|init-and-complete|general-aliases|bash-abbr)) selected[''$plugin]=1 ;; - (*) selected[''$plugin]= - esac - done - declare -a plugins_with_selection - while :; do - local PS3='Which plugins do you want to export/not export (enter a single number)? ' - plugins_with_selection=() - for plugin in "''${plugins[@]}"; do - if [[ ''${selected[''$plugin]} -eq 0 ]]; then - plugins_with_selection+=(''$'\033[31m'''$plugin''$'\033[m') - else - plugins_with_selection+=(''$'\033[32m'''$plugin''$'\033[m') - fi - done - 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[m'} - [[ -z ''$REPLY || ''$plugin = QUIT || ''$plugin = DONE ]] && break 2 - [[ -z ''$plugin ]] && { echo "Please enter a number (1-''$((''${#plugins[@]}+2)))."; continue; } - selected[''$plugin]=1-''${selected[''$plugin]} - break - done - [[ -z ''$REPLY || ''$plugin = QUIT || ''$plugin = DONE ]] && break - done - [[ -z ''$REPLY || ''$plugin = QUIT ]] && { echo "Aborting"; return; } - declare -a exported_plugins=() - for plugin in "''${!selected[@]}"; do - if [[ ''${selected[''$plugin]} -eq 1 ]]; then - exported_plugins+=(".bashrc.d/''$plugin") - fi - done - command rm -vf "exported_bashrc.tar.gz" - CDPATH= command cd - >/dev/null - CDPATH= command cd ~ - as_base64=''$(command tar -cvhzf - ".bashrc" "''${exported_plugins[@]}" | base64 -w0) - CDPATH= command cd - >/dev/null - as_base64_wrapped=''$(printf %s "''$as_base64" | sed 's#.\{,70\}#'\'''&'\'''\\\n#g' | sed ''\'''$s#\\$##') - command install -m775 /dev/stdin ~/bashrc_installer.sh <<-EOF - #! /usr/bin/env bash +export_bashrc () { + CDPATH= command cd ~/.bashrc.d/ || return + : 'Not using an associative array because those are not sorted!' + declare -ar plugins=(*) + [[ ''${#plugins[@]} -eq 0 ]] || [[ ''${#plugins} -eq 1 && ''${plugins[0]} = '*' ]] && return + local IFS=' ' + declare -Ai selected + declare plugin + for plugin in "''${plugins[@]}"; do + case "''$plugin" in + #-# Comment below case match if you don'\'''t want to have a pre-selection of plugins to export + #-# Requires `shopt -s extglob` + (+([0-9])-@(util|redact|prompt|docker-tools|funcs|init-and-complete|general-aliases|bash-abbr)) selected[''$plugin]=1 ;; + (*) selected[''$plugin]= + esac + done + declare -a plugins_with_selection + while :; do + local PS3='Which plugins do you want to export/not export (enter a single number)? ' + plugins_with_selection=() + for plugin in "''${plugins[@]}"; do + if [[ ''${selected[''$plugin]} -eq 0 ]]; then + plugins_with_selection+=(''$'\033[31m'''$plugin''$'\033[m') + else + plugins_with_selection+=(''$'\033[32m'''$plugin''$'\033[m') + fi + done + 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[m'} + [[ -z ''$REPLY || ''$plugin = QUIT || ''$plugin = DONE ]] && break 2 + [[ -z ''$plugin ]] && { echo "Please enter a number (1-''$((''${#plugins[@]}+2)))."; continue; } + selected[''$plugin]=1-''${selected[''$plugin]} + break + done + [[ -z ''$REPLY || ''$plugin = QUIT || ''$plugin = DONE ]] && break + done + [[ -z ''$REPLY || ''$plugin = QUIT ]] && { echo "Aborting"; return; } + declare -a exported_plugins=() + for plugin in "''${!selected[@]}"; do + if [[ ''${selected[''$plugin]} -eq 1 ]]; then + exported_plugins+=(".bashrc.d/''$plugin") + fi + done + command rm -vf "exported_bashrc.tar.gz" + CDPATH= command cd - >/dev/null + CDPATH= command cd ~ + as_base64=''$(command tar -cvhzf - ".bashrc" "''${exported_plugins[@]}" | base64 -w0) + CDPATH= command cd - >/dev/null + as_base64_wrapped=''$(printf %s "''$as_base64" | sed 's#.\{,70\}#'\'''&'\'''\\\n#g' | sed ''\'''$s#\\$##') + command install -m775 /dev/stdin ~/bashrc_installer.sh <<-EOF + #! /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; } - command -v bash >/dev/null 2>&1 || { printf 'bash must be installed!\\n'; exit 2; } + [ "\''$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; } - as_base64=''$as_base64_wrapped - tempdir=\''$(mktemp -d) - trap 'rm -rf -- "\''$tempdir"' EXIT - cd -- "\''$tempdir" - base64 -d <<< "\''$as_base64" | tar xzf - - create_dir_and_move () { - src=\''$1 - dst=\''$2 - [ ! -f "\''$src" ] && { printf '%s\\n' "Source argument '\''$src' is not a regular file"; return 1; } - dst_dir=\''$(dirname -- "\''${dst//[^!-~]/-}") - [ ! -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" || : - } - ask_overwrite () { - file1=\''$1 - file2=\''$2 - if [[ ! -e \''$file2 ]]; then - create_dir_and_move "\''$file1" "\''$file2" - elif [[ -d \''$file2 ]]; then - printf 'WARNING: Destination file %s is a directory! Skipping this file\\n' "\''${file2@Q}" - return - elif [[ ! -f \''$file2 ]]; then - printf 'WARNING: Destination file %s is not a regular file! Skipping this file\\n' "\''${file2@Q}" - return - elif ! diff -q "\''$file1" "\''$file2"; then - printf 'File to be installed (%s) differs from already installed file (%s)\\n' "\''${file1@Q}" "\''${file2@Q}" - read -p 'Press enter to show diff. ' || : - diff --color -u "\''$file1" "\''$file2" || : - 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' - 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 || : - printf 'Destination file %s will be overwritten. Continue? ' "\''${file2@Q}" - read || : - create_dir_and_move "\''$file1" "\''$file2" - else - printf 'File %s was not changed\\n' "\''${file2@Q}" - fi - } - ask_overwrite .bashrc ~/.bashrc - set +f - shopt -s nullglob dotglob - for file in .bashrc.d/*; do - ask_overwrite "\''$file" ~/"\''$file" - done - cd - >/dev/null - 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'; } - EOF - } + as_base64=''$as_base64_wrapped + tempdir=\''$(mktemp -d) + trap 'rm -rf -- "\''$tempdir"' EXIT + cd -- "\''$tempdir" + base64 -d <<< "\''$as_base64" | tar xzf - + create_dir_and_move () { + src=\''$1 + dst=\''$2 + [ ! -f "\''$src" ] && { printf '%s\\n' "Source argument '\''$src' is not a regular file"; return 1; } + dst_dir=\''$(dirname -- "\''${dst//[^!-~]/-}") + [ ! -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" || : + } + ask_overwrite () { + file1=\''$1 + file2=\''$2 + if [[ ! -e \''$file2 ]]; then + create_dir_and_move "\''$file1" "\''$file2" + elif [[ -d \''$file2 ]]; then + printf 'WARNING: Destination file %s is a directory! Skipping this file\\n' "\''${file2@Q}" + return + elif [[ ! -f \''$file2 ]]; then + printf 'WARNING: Destination file %s is not a regular file! Skipping this file\\n' "\''${file2@Q}" + return + elif ! diff -q "\''$file1" "\''$file2"; then + printf 'File to be installed (%s) differs from already installed file (%s)\\n' "\''${file1@Q}" "\''${file2@Q}" + read -p 'Press enter to show diff. ' || : + diff --color -u "\''$file1" "\''$file2" || : + 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' + 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 || : + printf 'Destination file %s will be overwritten. Continue? ' "\''${file2@Q}" + read || : + create_dir_and_move "\''$file1" "\''$file2" + else + printf 'File %s was not changed\\n' "\''${file2@Q}" + fi + } + ask_overwrite .bashrc ~/.bashrc + set +f + shopt -s nullglob dotglob + for file in .bashrc.d/*; do + ask_overwrite "\''$file" ~/"\''$file" + done + cd - >/dev/null + 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'; } + EOF +} - #-#################################### binds ##################################### - bind '"\e\C-r": kill-region' \ - '"\C-x\C-k": kill-whole-line' \ - '"\e\C-p": history-search-backward' \ - '"\e\C-n": history-search-forward' \ - '"\eP": history-substring-search-backward' \ - '"\eN": history-substring-search-forward' \ - '"\C-x\C-d": "\e#\C-m exit\C-m"' \ - '"\ej": "\C-e |& tac | \\cat -n | tac | \\cat -n | less -SFXR\C-m"' \ - '"\eX": ">/dev/null "' \ - '"\t": menu-complete' \ - '"\e[Z": menu-complete-backward' \ - '"\C-x\C-r": forward-search-history' \ - 'set show-all-if-ambiguous on' \ - 'set completion-query-items 0' \ - 'set menu-complete-display-prefix on' \ - 'set colored-completion-prefix on' \ - 'set colored-stats on' \ - 'set mark-modified-lines on' \ - 'set revert-all-at-newline on' \ - 'set blink-matching-paren on' \ - 'set history-preserve-point on' +#-#################################### binds ##################################### +bind '"\e\C-r": kill-region' \ + '"\C-x\C-k": kill-whole-line' \ + '"\e\C-p": history-search-backward' \ + '"\e\C-n": history-search-forward' \ + '"\eP": history-substring-search-backward' \ + '"\eN": history-substring-search-forward' \ + '"\C-x\C-d": "\e#\C-m exit\C-m"' \ + '"\ej": "\C-e |& tac | \\cat -n | tac | \\cat -n | less -SFXR\C-m"' \ + '"\eX": ">/dev/null "' \ + '"\t": menu-complete' \ + '"\e[Z": menu-complete-backward' \ + '"\C-x\C-r": forward-search-history' \ + 'set show-all-if-ambiguous on' \ + 'set completion-query-items 0' \ + 'set menu-complete-display-prefix on' \ + 'set colored-completion-prefix on' \ + 'set colored-stats on' \ + 'set history-preserve-point on' - #-################################## autoclear ################################### - #-# TODO +#-################################## autoclear ################################### +#-# TODO - #-# prank () { - #-# line_up_until_cursor=''${READLINE_LINE::READLINE_POINT} - #-# line_after_cursor=''${READLINE_LINE:READLINE_POINT} - #-# if [[ ''$((RANDOM % 5)) -eq 0 ]]; then - #-# READLINE_LINE="''$line_up_until_cursor''$line_after_cursor" - #-# #READLINE_LINE="''$line_up_until_cursor''${1^}''$line_after_cursor" - #-# READLINE_POINT=''$((READLINE_POINT+1)) - #-# else - #-# READLINE_LINE="''$line_up_until_cursor''$1''$line_after_cursor" - #-# READLINE_POINT=''$((READLINE_POINT+1)) - #-# fi - #-# } - #-# bind -x '" ": prank " "' - #-# bind -x '"a": prank "a"' +#-# prank () { +#-# line_up_until_cursor=''${READLINE_LINE::READLINE_POINT} +#-# line_after_cursor=''${READLINE_LINE:READLINE_POINT} +#-# if [[ ''$((RANDOM % 5)) -eq 0 ]]; then +#-# READLINE_LINE="''$line_up_until_cursor''$line_after_cursor" +#-# #READLINE_LINE="''$line_up_until_cursor''${1^}''$line_after_cursor" +#-# READLINE_POINT=''$((READLINE_POINT+1)) +#-# else +#-# READLINE_LINE="''$line_up_until_cursor''$1''$line_after_cursor" +#-# READLINE_POINT=''$((READLINE_POINT+1)) +#-# fi +#-# } +#-# bind -x '" ": prank " "' +#-# bind -x '"a": prank "a"' ''; }; }