aboutsummaryrefslogtreecommitdiff
path: root/.local/bin
diff options
context:
space:
mode:
Diffstat (limited to '.local/bin')
-rwxr-xr-x.local/bin/bashhistory18
-rwxr-xr-x.local/bin/broken-links10
-rwxr-xr-x.local/bin/camera-gstream2
-rwxr-xr-x.local/bin/camera-mplayer28
-rwxr-xr-x.local/bin/dropdown-terminal2
-rwxr-xr-x.local/bin/fzf-doc44
-rwxr-xr-x.local/bin/lxc-build12
-rwxr-xr-x.local/bin/mplayer-camera5
-rwxr-xr-x.local/bin/nixos-test2
-rwxr-xr-x.local/bin/pass-import-csv2
-rwxr-xr-x.local/bin/picospeaker14
-rwxr-xr-x.local/bin/plumber97
-rwxr-xr-x.local/bin/plumber-dmenu42
-rwxr-xr-x.local/bin/portmanteau44
-rwxr-xr-x.local/bin/say23
-rwxr-xr-x.local/bin/seance125
-rwxr-xr-x.local/bin/sfeed_fzf24
-rwxr-xr-x.local/bin/tidy-url2
-rwxr-xr-x.local/bin/virtual-camera7
-rwxr-xr-x.local/bin/wrappers/aspell2
-rwxr-xr-x.local/bin/wrappers/audacity2
-rwxr-xr-x.local/bin/wrappers/ghci2
-rwxr-xr-x.local/bin/wrappers/git12
-rwxr-xr-x.local/bin/wrappers/google-chrome (renamed from .local/bin/wrappers/google-chrome-stable)0
-rwxr-xr-x.local/bin/wrappers/guile2
-rwxr-xr-x.local/bin/wrappers/mix2
-rwxr-xr-x.local/bin/wrappers/mocp2
-rwxr-xr-x.local/bin/wrappers/nix-index16
-rwxr-xr-x.local/bin/wrappers/sbcl2
-rwxr-xr-x.local/bin/wrappers/stalonetray6
-rwxr-xr-x.local/bin/wrappers/swc2
-rwxr-xr-x.local/bin/wrappers/tidy3
32 files changed, 385 insertions, 171 deletions
diff --git a/.local/bin/bashhistory b/.local/bin/bashhistory
index 6320834..98f1d33 100755
--- a/.local/bin/bashhistory
+++ b/.local/bin/bashhistory
@@ -4,16 +4,14 @@ directory="$XDG_DATA_HOME/bash"
persist="$directory/history"
default="$HOME/.bash_history"
current="$HISTFILE"
+lockfile='/tmp/bashhistory_Ri5ki9ei.lock'
_prune() {
- sed --in-place '/^#/d' "$1" && # Remove all timestamps.
- sed --in-place '/......../!d' "$1" && # Remove all commands 8 characters or less.
- sort --unique "$1" | sponge "$1" # Remove all duplicate entries.
+ sed --in-place '/^#/d;/^ /d;/^\t/d;/.........../!d' "$1" && # Remove all timestamps, lines beginning with tabs/spaces, and commands less than 11 characters
+ sort --unique "$1" > "$1.tmp" && # Remove all duplicate entries.
+ mv --force "$1.tmp" "$1" > /dev/null 2>&1
}
-mkdir --parents "$directory"
-touch "$current"
-
_migrate() {
[ -f "$default" ] &&
cat "$default" >>"$persist" &&
@@ -23,9 +21,17 @@ _migrate() {
_store() {
cat "$current" >> "$persist" &&
+ cat "$current" >> "$persist.bak" &&
_prune "$persist" &&
true > "$current"
}
+[ -f "$lockfile" ] && exit;
+
+mkdir --parents "$directory"
+touch "$current" "$lockfile"
+
+trap 'rm "$lockfile" > /dev/null 2>&1; trap - EXIT; exit' EXIT INT HUP
+
_migrate || true
_store
diff --git a/.local/bin/broken-links b/.local/bin/broken-links
index 20da6ef..02db6a0 100755
--- a/.local/bin/broken-links
+++ b/.local/bin/broken-links
@@ -1,7 +1,13 @@
#!/bin/sh -eu
-lynx -nocolor -dump -listonly "${1:-https://example.com}" |
- grep --color="never" "\." |
+lynx \
+ -nocolor \
+ -dump \
+ -listonly \
+ "${1:-https://example.com}" |
+ grep \
+ --color="never" \
+ "\." |
while read -r line; do
url=${line#*. }
head=$(wget \
diff --git a/.local/bin/camera-gstream b/.local/bin/camera-gstream
new file mode 100755
index 0000000..3b54380
--- /dev/null
+++ b/.local/bin/camera-gstream
@@ -0,0 +1,2 @@
+#!/bin/sh -eu
+gst-launch-1.0 -v v4l2src device=/dev/video0 ! glimagesink
diff --git a/.local/bin/camera-mplayer b/.local/bin/camera-mplayer
new file mode 100755
index 0000000..73f592f
--- /dev/null
+++ b/.local/bin/camera-mplayer
@@ -0,0 +1,28 @@
+#!/bin/sh -eu
+
+program=$(basename "$0")
+option=${1:-0@9}
+real=${option%%@*}
+virtual=${option#*@}
+
+{
+ [ "$option" = "help" ] ||
+ [ "$option" = "-help" ] ||
+ [ "$option" = "--help" ]
+} &&
+ printf '%s 0@9\n# /dev/video0 -> /dev/video9\n' "$program" &&
+ exit
+
+{
+ [ "$option" = "kill" ] ||
+ [ "$option" = "-kill" ] ||
+ [ "$option" = "--kill" ]
+} &&
+ killall mplayer &&
+ killall mplayer &&
+ killall ffmpeg &&
+ exit
+
+ffmpeg -i "/dev/video$real" -f v4l2 -vcodec rawvideo -pix_fmt rgb24 "/dev/video$virtual" &
+sleep 2
+mplayer tv:// -tv "driver=v4l2:device=/dev/video$virtual" -fps 15
diff --git a/.local/bin/dropdown-terminal b/.local/bin/dropdown-terminal
index 46f83ea..a41ad46 100755
--- a/.local/bin/dropdown-terminal
+++ b/.local/bin/dropdown-terminal
@@ -1,7 +1,7 @@
#!/bin/sh -eu
name=dropdown-terminal
state=/tmp/"$name"_lQ5GnvRpQ6
-terminal="urxvt -pe tabbed -geometry 150x20 -title $name"
+terminal="urxvt -geometry 150x20 -title $name"
[ ! -f $state ] && {
$terminal && sed --in-place '1s/.*/1/' "$state" &
diff --git a/.local/bin/fzf-doc b/.local/bin/fzf-doc
deleted file mode 100755
index 6f51c08..0000000
--- a/.local/bin/fzf-doc
+++ /dev/null
@@ -1,44 +0,0 @@
-#!/bin/sh -eu
-
-cache=$HOME/.cache/fzf-doc
-documentation=/etc/documentation
-
-fzf_doc_preview() {
- file=$1
- extension=${file##*.}
- case "$extension" in
- md) mdcat -l "$file" ;;
- html) w3m -dump "$file" ;;
- pdf) pdftotext "$file" - ;;
- *) grep -hi -B 10 -A 10 . "${file}" ;;
- esac
-}
-
-if [ ! -f "$cache" ] || test "$cache" -ot "$documentation"; then
- grep -lRi \
- --include=*.md \
- --include=*.txt \
- --include=*.pdf \
- --include=*.html \
- --include=*.yml \
- --include=*.yaml \
- . /etc/documentation > "$cache";
-fi
-
-[ "${1-}" = "--preview" ] && fzf_doc_preview "${2-}" && exit;
-
-grep -i "${1-.}" "$cache" \
- | fzf --preview "fzf-doc --preview {}" \
- | while read -r file
-
-do
- extension=${file##*.}
- case "$extension" in
- md) mdcat -cl "$file" | vim - ;;
- html) w3m -dump "$file" | vim - ;;
- pdf) pdftotext "$file" - | vim - ;;
- yml) vim "$file" ;;
- yaml) vim "$file" ;;
- *) vim "$file" ;;
- esac
-done
diff --git a/.local/bin/lxc-build b/.local/bin/lxc-build
index c9e41f5..9e6b5eb 100755
--- a/.local/bin/lxc-build
+++ b/.local/bin/lxc-build
@@ -17,14 +17,6 @@ help = unlines
, ""
, "SYNOPSIS"
, ""
- , " Create an lxc container named rockylinux with the specified Dockerfile"
- , ""
- , " lxc-build --name rockylinux rockylinux8.dockerfile"
- , ""
- , " Start the container after building"
- , ""
- , " lxc-build --start --name rockylinux rockylinux8.dockerfile"
- , ""
, " Replace a container of the same name with the new build"
, ""
, " lxc-build --replace --name rockylinux rockylinux8.dockerfile"
@@ -35,11 +27,7 @@ help = unlines
, ""
, "COMMANDS"
, ""
- , " -n, -name, --name The container name"
- , " -c, -create, --create Create the container"
, " -h, -help, --help Shows this help menu"
- , " -u, -user-map, --user-map User mapping id"
- , " -g, -group-map, --group-map Group mapping id"
, " -r, -replace, --replace Replace container with new build"
, " -v, -version, --version Prints program version"
]
diff --git a/.local/bin/mplayer-camera b/.local/bin/mplayer-camera
deleted file mode 100755
index 996c79f..0000000
--- a/.local/bin/mplayer-camera
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/sh -eu
-program=$(basename "$0")
-[ "$#" = 0 ] && printf '%s 9\n# /dev/video9 -> mplayer\n' "$program" && exit
-[ "$1" = kill ] && killall --quiet -9 mplayer
-mplayer tv:// -tv "driver=v4l2:device=/dev/video$1" -fps 15
diff --git a/.local/bin/nixos-test b/.local/bin/nixos-test
index c850d13..1fedf89 100755
--- a/.local/bin/nixos-test
+++ b/.local/bin/nixos-test
@@ -30,6 +30,6 @@ for configuration in "$@"; do
NIXPKGS_ALLOW_UNFREE=1 \
NIXPKGS_ALLOW_UNSUPPORTED_SYSTEM=1
- nixos-rebuild --fast --option substituters 'https://cache.nixos.org' dry-build
+ nixos-rebuild --fast --option substituters 'https://cache.nixos.org' --option builders '' dry-build
done;
diff --git a/.local/bin/pass-import-csv b/.local/bin/pass-import-csv
index df7c369..7682657 100755
--- a/.local/bin/pass-import-csv
+++ b/.local/bin/pass-import-csv
@@ -2,7 +2,7 @@
csv_file="$HOME/.cache/k.csv"
import_directory="$HOME/.config/pass";
-target_directory="$import_directory/keepassxc";
+target_directory="$import_directory/secrets";
{ [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ] || [ "$#" = 0 ]; } && printf 'Usage: %s /path/to/keepassxc/database\n' "$(basename "$0")" && exit
diff --git a/.local/bin/picospeaker b/.local/bin/picospeaker
deleted file mode 100755
index 55a1b7b..0000000
--- a/.local/bin/picospeaker
+++ /dev/null
@@ -1,14 +0,0 @@
-#!/bin/sh -eu
-
-audio=/tmp/picospeaker-jfgOUcZdWu.wav
-text=${1:-Nothing to read.}
-
-[ "${1-}" = "echo" ] && picospeaker "${2-}" "${1-}" && exit;
-
-pico2wave -w "$audio" "$text";
-
-[ "${2-}" = "echo" ] \
- && ffmpeg -y -i "$audio" -map 0 -c:v copy -af aecho=1:1:50:0.5,atempo=0.85 "$audio.mp3" \
- && mplayer -really-quiet "$audio.mp3" && exit;
-
-mplayer -really-quiet "$audio";
diff --git a/.local/bin/plumber b/.local/bin/plumber
index e2c9911..88aa80c 100755
--- a/.local/bin/plumber
+++ b/.local/bin/plumber
@@ -2,7 +2,7 @@
defmodule Settings do
def terminal do
- "urxvt"
+ System.get_env("TERMINAL")
end
end
@@ -14,22 +14,30 @@ defmodule ArgParser do
options = [
camelize: "text->camelize",
+ case_lower: "case->lower",
+ case_title: "case->title",
+ case_titleize: "case->titleize",
+ case_upper: "case->upper",
date8601: "text->date8601",
dictionary: "word->dictionary",
- first_letter: "text->first-letter-words",
- kjv_lookup: "verse->kjv",
+ jumbleize: "text->jumbleize",
+ kjv: "verse->kjv",
+ letterize: "text->letterize",
lorem_paragraph: "lorem->paragraph",
lorem_title: "lorem->title",
- lower_case: "case->lower",
- single_line: "text->single-line",
+ reverse_letters: "reverse->letters",
+ reverse_words: "reverse->words",
+ singleline: "text->singleline",
slugize: "text->slugize",
- title_case: "case->title",
- title_case_strict: "case->title-strict"
+ urlize: "text->urlize",
]
cond do
- opts[:option] == options[:first_letter] ->
- TextPlumber.firstLetterOfWordsIn(opts[:text]) |> IO.binwrite()
+ opts[:option] == options[:letterize] ->
+ TextPlumber.firstLetterOfWordsIn(opts[:text]) |> IO.puts()
+
+ opts[:option] == options[:urlize] ->
+ URI.encode(opts[:text]) |> IO.puts()
opts[:option] == options[:slugize] ->
opts[:text]
@@ -40,7 +48,7 @@ defmodule ArgParser do
|> String.replace(~r/[[:space:]]+/, "-")
|> String.replace(~r/[[:punct:]]+/, "-")
|> String.trim("-")
- |> IO.binwrite()
+ |> IO.puts()
opts[:option] == options[:camelize] ->
opts[:text]
@@ -50,23 +58,44 @@ defmodule ArgParser do
|> Macro.camelize()
|> String.replace(~r/[[:space:]]+/, "")
|> String.replace(~r/[[:punct:]]+/, "")
- |> IO.binwrite()
+ |> IO.puts()
+
+ opts[:option] == options[:singleline] ->
+ TextPlumber.singleLineOf(opts[:text]) |> IO.puts()
+
+ opts[:option] == options[:jumbleize] ->
+ String.split(opts[:text] |> String.replace(~r/[[:punct:]]+/, ""))
+ |> Enum.map(&String.codepoints/1)
+ |> Enum.map(&TextPlumber.jumble/1)
+ |> Enum.map(&Enum.join/1)
+ |> Enum.join(" ")
+ |> IO.puts()
- opts[:option] == options[:single_line] ->
- TextPlumber.singleLineOf(opts[:text]) |> IO.binwrite()
+ opts[:option] == options[:reverse_words] ->
+ TextPlumber.reverse(opts[:text]) |> IO.puts()
+
+ opts[:option] == options[:reverse_letters] ->
+ String.split(opts[:text])
+ |> Enum.map(&String.codepoints/1)
+ |> Enum.map(&Enum.reverse/1)
+ |> Enum.join(" ")
+ |> IO.puts()
opts[:option] == options[:dictionary] ->
TextPlumber.dictionOfFirstWordIn(opts[:text])
- TextPlumber.firstWordOf(opts[:text]) |> IO.binwrite()
+ TextPlumber.firstWordOf(opts[:text]) |> IO.puts()
+
+ opts[:option] == options[:case_title] ->
+ opts[:text] |> to_string |> TextPlumber.titleCaseOf() |> IO.puts()
- opts[:option] == options[:title_case] ->
- opts[:text] |> to_string |> TextPlumber.titleCaseOf() |> IO.binwrite()
+ opts[:option] == options[:case_titleize] ->
+ String.downcase(opts[:text]) |> TextPlumber.titleCaseOf() |> IO.puts()
- opts[:option] == options[:title_case_strict] ->
- String.downcase(opts[:text]) |> TextPlumber.titleCaseOf() |> IO.binwrite()
+ opts[:option] == options[:case_lower] ->
+ String.downcase(opts[:text]) |> IO.puts()
- opts[:option] == options[:lower_case] ->
- String.downcase(opts[:text]) |> IO.binwrite()
+ opts[:option] == options[:case_upper] ->
+ String.upcase(opts[:text]) |> IO.puts()
opts[:option] == options[:lorem_title] ->
TextPlumber.loremTitle()
@@ -77,7 +106,7 @@ defmodule ArgParser do
opts[:option] == options[:date8601] ->
TextPlumber.date8601()
- opts[:option] == options[:kjv_lookup] ->
+ opts[:option] == options[:kjv] ->
System.cmd(Settings.terminal(), [
"-e",
"sh",
@@ -85,7 +114,7 @@ defmodule ArgParser do
"kjv #{TextPlumber.singleLineOf(opts[:text])}"
])
- TextPlumber.singleLineOf(opts[:text]) |> IO.binwrite()
+ TextPlumber.singleLineOf(opts[:text]) |> IO.puts()
true ->
Enum.each(options, fn {_, value} -> IO.puts(value) end)
@@ -95,8 +124,8 @@ end
defmodule TextPlumber do
def firstLetterOfWordsIn(text) do
- String.replace(text, ~r/(\w)\w*/, "\\1", global: true)
- |> String.replace(~r/\s\s+/, " ", global: true)
+ String.replace(text, ~r/(\w)\w*/u, "\\1", global: true)
+ |> String.replace(~r/\s\s+/u, " ", global: true)
end
def singleLineOf(text) do
@@ -108,6 +137,10 @@ defmodule TextPlumber do
String.split(text) |> List.first()
end
+ def reverse(text) do
+ String.split(text) |> Enum.reverse |> Enum.join(" ")
+ end
+
def titleCaseOf(text) do
text |> String.split() |> Enum.map(fn word -> :string.titlecase(word) end) |> Enum.join(" ")
end
@@ -141,6 +174,22 @@ defmodule TextPlumber do
])
end
+ def jumble(list) do
+ if length(list) == 1 do
+ list
+ else
+ head = Enum.take(list, 1)
+ tail = Enum.take(list, -1)
+ list = list |> Enum.drop(-1) |> Enum.drop(1)
+
+ if length(list) == 2 do
+ head ++ (list |> Enum.reverse()) ++ tail
+ else
+ head ++ (list |> Enum.shuffle()) ++ tail
+ end
+ end
+ end
+
def date8601() do
{string, _return} =
System.cmd("date", [
diff --git a/.local/bin/plumber-dmenu b/.local/bin/plumber-dmenu
index 262d099..ea141c2 100755
--- a/.local/bin/plumber-dmenu
+++ b/.local/bin/plumber-dmenu
@@ -1,22 +1,32 @@
#!/bin/sh -eu
+
options="\
-text->camelize
-text->date8601
-word->dictionary
-text->first-letter-words
-verse->kjv
+case->lower
+case->title
+case->titleize
+case->upper
lorem->paragraph
lorem->title
-case->lower
-text->single-line
+reverse->letters
+reverse->words
+text->camelize
+text->date8601
+text->jumbleize
+text->letterize
+text->singleline
text->slugize
-case->title
+text->urlize
+verse->kjv
+word->dictionary
"
-printf "%s" "$options" \
- | dmenu -i -b -p ⠀:::⠀plumber⠀::: \
- | while read -r option
- do
- text=$(plumber --option "$option" --text "$(xsel -o)")
- { notify-send "$text" && printf "%s" "$text" | xsel -ib; } \
- || notify-send "Plumber: Clipboard selection is empty"
- done
+
+selection=$(
+ printf "%s" "$options" | dmenu -i -b -p ⠀:::⠀plumber⠀::: ||
+ { [ "$?" = 127 ] && kill -15 "$$" && "$0"; }
+)
+
+printf "%s\n" "$selection" | while read -r option; do
+ text=$(plumber --option "$option" --text "$(xsel -o)")
+ { notify-send "$text" && printf "%s" "$text" | xsel -ib; } ||
+ notify-send "Plumber: Clipboard selection is empty"
+done
diff --git a/.local/bin/portmanteau b/.local/bin/portmanteau
index a3788c8..9ce9dc3 100755
--- a/.local/bin/portmanteau
+++ b/.local/bin/portmanteau
@@ -1,10 +1,38 @@
-#!/bin/sh -eu
-word=$(diceware --no-caps --num 2 --delimiter " ")
-first=${word%??? *}
-second=${word#* ???}
+#!/usr/bin/env bash
+set -eu
-# TODO: Maybe manipulate adjacent consonants and vowels at the word merge boundary.
-# barric[ade] [hea]dlock
-# barri[cd]lock -> barrilock
+minimum=4
+words=$(diceware --no-caps --num 2 --delimiter " ")
+first=${words%??? *}
+second=${words#* ???}
-printf '%s\n%s%s\n' "$word" "$first" "$second"
+[ ${#first} -lt $minimum ] && "$0" && exit
+[ ${#second} -lt $minimum ] && "$0" && exit
+
+vowels="a e i o u"
+sounds="h r w y"
+consonants="b c d f g j k l m n p q s t v x z"
+suffix=${first: -1:1}
+prefix=${second: 0:1}
+digraph="$suffix$prefix"
+compressed="${first: 0:-1}${second: 1}"
+
+printf 'words: %s\n' "$words"
+printf 'digraph: %s\n' "$digraph"
+printf 'merge: %s%s\n' "$first" "$second"
+printf 'compressed: %s\n\n' "$compressed"
+
+Grammar() {
+ for left in $consonants; do
+ [ "$suffix" = "$left" ] &&
+ {
+ for right in $consonants; do
+ [ "$prefix" = "$right" ] &&
+ printf '%s\n' "$compressed" &&
+ exit
+ done
+ }
+ done || printf '%s%s\n' "$first" "$second"
+}
+
+Grammar
diff --git a/.local/bin/say b/.local/bin/say
new file mode 100755
index 0000000..4537669
--- /dev/null
+++ b/.local/bin/say
@@ -0,0 +1,23 @@
+#!/bin/sh -eu
+
+audio=/tmp/speak-jfgOUcZdWu.wav
+text=${1:-Nothing to say.}
+
+[ "${1-}" = "echo" ] && $0 "${2-}" "${1-}" && exit;
+
+printf '%s' "$text" |
+ piper \
+ --model "$HOME/.local/share/piper/voices/en_GB-jenny_dioco-medium.onnx" \
+ --output_file $audio
+
+[ "${2-}" = "echo" ] &&
+ ffmpeg \
+ -y \
+ -i "$audio" \
+ -map 0 \
+ -c:v copy \
+ -af aecho=1:1:50:0.5,atempo=0.85 "$audio.mp3" &&
+ mplayer -really-quiet "$audio.mp3" &&
+ exit;
+
+mplayer -really-quiet "$audio";
diff --git a/.local/bin/seance b/.local/bin/seance
index 9c708cf..5b9160e 100755
--- a/.local/bin/seance
+++ b/.local/bin/seance
@@ -5,7 +5,7 @@ directory=$HOME/.config/seance
session=$directory/session
spirits=$directory/spirits
-help() {
+Help() {
printf \
"
Usage: %s [FLAGS]... [ARGUMENT]...
@@ -14,48 +14,119 @@ The program $program shall save and restore
running programs in a desktop session according
to a specified configuration file.
-Add a program string, one per line,
-to $spirits to
-save programs in current session.
+Add a program command, one per line, in
+$spirits
+to save to current session.
-Session: $session
-Configuration: $spirits
+Session: $session
+Config: $spirits
Command List:
- $program commit Saves current running programs to session.
- $program list List saved programs.
- $program config View current configuration.
- $program search Search for a running program.
- $program start Restore previous session.
- $program -h --help Show this help menu.
+ $program commit Saves programs in config to session.
+ $program list List session commands.
+ $program config View programs to be saved to session.
+ $program windows List running programs
+ $program search Search for a running program.
+ $program start Restore previous session.
+ $program edit session Edit session commands.
+ $program edit config Edit programs in config.
+ $program -h --help Show this help menu.
" "$program";
}
-{ [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ] || [ "$#" = 0 ]; } && help && exit;
+Commit() {
+
+windows=$(wmctrl -Glp)
+date=$(date --utc '+%a-%b-%d-%H:%M:%S-%Y')
+
+IFS='
+'
+
+mv "$1" "$1-$date"
+
+true > "$1"
+
+for window in $windows; do
+
+pid=$(printf "%s" "$window" | awk '{ print $3 }')
+desktop=$(printf "%s" "$window" | awk '{ print $2 }')
+title=$(printf "%s" "$window" | tr -s ' ' | cut -d ' ' -f 9-)
+dimensions=$(printf "%s" "$window" | awk '{ print $4, ",", $5, ",", $6, ",", $7 }' | tr -d ' ')
+[ "$pid" = "0" ] && continue;
+process=$(ps -ww -p "$pid" -o command | tail -n 1)
+
+while read -r spirit; do
+ case "$process" in
+ *$spirit*) process=$spirit ;;
+ esac
+done < "$spirits"
+
+commands=$(printf "%s" "$title" | cut -d ' ' -f 3- | sed "s|'|'\\\\\\\''|g")
+
+[ -z "$commands" ] && commands="true"
+
+mime="$(file --mime-type "$title" | awk '{ print $2 }')"
+directory=$(printf "%s" "$title" | cut -d ' ' -f 2)
+
+case "$mime" in
+application/pdf) process="zathura $title" ;;
+esac
+
+case "$title" in
+*term:*) process="\$TERMINAL -e sh -c 'cd $directory && $commands; \$SHELL'" ;;
+esac
+
+case "$title" in
+*vim:*) process="\$TERMINAL -e sh -c '\$EDITOR $directory; \$SHELL'" ;;
+esac
+
+case "$title" in
+*\[Scratch\]*) continue ;;
+esac
+
+case "$commands" in
+*$program*) continue ;;
+esac
+
+grep "$process" "$1" > /dev/null 2>&1 && continue
+
+printf "%s\n" "$process"
+
+printf "\
+$process > /dev/null 2>&1 & disown
+i=0; until wmctrl -Glp | grep -i \"\$!\"; do i=\$((i+1)); [ \$i -gt 150 ] && break; done
+wmctrl -i -r \"\$(wmctrl -Glp | awk -v pid=\"\$!\" '\$0 ~ pid { print \$1 }')\" -t $desktop
+wmctrl -i -r \"\$(wmctrl -Glp | awk -v pid=\"\$!\" '\$0 ~ pid { print \$1 }')\" -e '0,$dimensions'
+
+" >> "$1"
+done
+}
+
+{ [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ] || [ "$#" = 0 ]; } && Help && exit;
mkdir --parents "$directory"
touch "$spirits"
-commit() {
- ps aux |
- awk '$1 == "'"$(whoami)"'" { $1=$2=$3=$4=$5=$6=$7=$8=$9=$10=""; print $0 }' |
- awk '!deduplicate[$0]++ { $1=$1; printf "%s &\n", $0 }'
-}
+[ "${1:-}" = "commit" ] \
+ && { Commit "$session" || printf "Warning empty or failed session file!\n"; } \
+ && printf '\nSession %s canonized.\n\n' "$session" \
+ && exit
+
+[ "${1:-}" = "list" ] && cat "$session" && exit;
+
+[ "${1:-}" = "config" ] && cat "$spirits" && exit;
-[ "${1:-}" = "commit" ] &&
- { commit | grep --file="$spirits" > "$session" || printf "Warning empty or failed session file!\n"; } &&
- printf 'Session %s canonized.\n' "$session" &&
- exit
+[ "${1:-}" = "windows" ] && wmctrl -Glp && exit;
-[ "${1:-}" = "list" ] && cat "$session" && exit;
+[ "${1:-}" = "search" ] && wmctrl -Glp | grep --ignore-case -- "$2" && exit;
-[ "${1:-}" = "config" ] && cat "$spirits" && exit;
+[ "${1:-}" = "start" ] && sh "$session" && exit;
-[ "${1:-}" = "search" ] && commit | grep --ignore-case -- "$2" | head --lines=1 && exit;
+[ "${1:-}" = "edit" ] && [ "${2:-}" = "session" ] && $EDITOR "$session" && exit;
-[ "${1:-}" = "start" ] && /bin/sh "$session" && exit;
+[ "${1:-}" = "edit" ] && [ "${2:-}" = "config" ] && $EDITOR "$spirits" && exit;
-help && printf "Error: Unknown argument '%s'.\n\n" "$@" && exit 1;
+Help && printf "Error: Unknown argument '%s'.\n\n" "$@" && exit 1;
diff --git a/.local/bin/sfeed_fzf b/.local/bin/sfeed_fzf
new file mode 100755
index 0000000..5c432b8
--- /dev/null
+++ b/.local/bin/sfeed_fzf
@@ -0,0 +1,24 @@
+#!/bin/sh -eu
+
+config=$XDG_CONFIG_HOME/sfeed/sfeedrc
+unread=$XDG_CONFIG_HOME/sfeed/unread
+read=$XDG_CONFIG_HOME/sfeed/read
+feeds=$XDG_CONFIG_HOME/sfeed/feeds
+
+touch "$read"
+
+sfeed_update "$config" || true
+
+sfeed_plain_function() {
+ sfeed_plain "$feeds"/* |
+ sed 's/^[N ]\+//g' |
+ sort --reverse |
+ grep --invert-match --fixed-strings --file "$read" > "$unread"
+}
+
+sfeed_plain_function
+fzf < "$unread" \
+ --multi \
+ --info hidden \
+ --bind "enter:execute(firefox \$(echo {} | awk '{ print \$NF }'))" \
+ --bind "ctrl-d:reload(echo {} >> $read && { sfeed_plain $feeds/* | sed 's/^[N ]\+//g' | sort --reverse | grep --invert-match --fixed-strings --file $read > $unread && cat $unread; })"
diff --git a/.local/bin/tidy-url b/.local/bin/tidy-url
new file mode 100755
index 0000000..2aa400b
--- /dev/null
+++ b/.local/bin/tidy-url
@@ -0,0 +1,2 @@
+#!/bin/sh -eu
+curl --silent "$1" | tidy -config "$HOME/.config/tidy/url.conf"
diff --git a/.local/bin/virtual-camera b/.local/bin/virtual-camera
deleted file mode 100755
index 0ea6f76..0000000
--- a/.local/bin/virtual-camera
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh -eu
-program=$(basename "$0")
-option=${1:-}
-real=${option%%@*}
-virtual=${option#*@}
-[ "$#" = 0 ] && printf '%s 0@9\n# /dev/video0 -> /dev/video9\n' "$program" && exit;
-ffmpeg -i "/dev/video$real" -f v4l2 -vcodec rawvideo -pix_fmt rgb24 "/dev/video$virtual"
diff --git a/.local/bin/wrappers/aspell b/.local/bin/wrappers/aspell
new file mode 100755
index 0000000..78fddd0
--- /dev/null
+++ b/.local/bin/wrappers/aspell
@@ -0,0 +1,2 @@
+#!/bin/sh -eu
+$(which aspell --all | grep --invert-match "local/bin" | head -n 1) --conf "$HOME/.config/aspell/aspell.conf" "$@";
diff --git a/.local/bin/wrappers/audacity b/.local/bin/wrappers/audacity
new file mode 100755
index 0000000..fead7fd
--- /dev/null
+++ b/.local/bin/wrappers/audacity
@@ -0,0 +1,2 @@
+#!/bin/sh -eu
+tenacity "$@"
diff --git a/.local/bin/wrappers/ghci b/.local/bin/wrappers/ghci
new file mode 100755
index 0000000..75f3577
--- /dev/null
+++ b/.local/bin/wrappers/ghci
@@ -0,0 +1,2 @@
+#!/bin/sh -eu
+HOME=$HOME/.config && $(which ghci --all | grep --invert-match "local/bin" | head -n 1) "$@";
diff --git a/.local/bin/wrappers/git b/.local/bin/wrappers/git
new file mode 100755
index 0000000..d321469
--- /dev/null
+++ b/.local/bin/wrappers/git
@@ -0,0 +1,12 @@
+#!/bin/sh -eu
+
+GIT=$(which git --all | grep --invert-match "local/bin" | head --lines 1)
+GIT_COMMITTER_DATE="$(date --utc --date '0' '+%a %b %d %H:%M:%S %Y %z')"
+
+export GIT_COMMITTER_DATE
+
+[ "${1:-}" = "commit" ] && export DATE=1 && $GIT "$@" --date="$GIT_COMMITTER_DATE"
+
+[ "${DATE:-}" = 1 ] && exit
+
+$GIT "$@"
diff --git a/.local/bin/wrappers/google-chrome-stable b/.local/bin/wrappers/google-chrome
index b9c406f..b9c406f 100755
--- a/.local/bin/wrappers/google-chrome-stable
+++ b/.local/bin/wrappers/google-chrome
diff --git a/.local/bin/wrappers/guile b/.local/bin/wrappers/guile
new file mode 100755
index 0000000..1a5b4d8
--- /dev/null
+++ b/.local/bin/wrappers/guile
@@ -0,0 +1,2 @@
+#!/bin/sh -eu
+HOME=$HOME/.config && $(which guile --all | grep --invert-match "local/bin" | head --lines 1) "$@";
diff --git a/.local/bin/wrappers/mix b/.local/bin/wrappers/mix
new file mode 100755
index 0000000..c687a2d
--- /dev/null
+++ b/.local/bin/wrappers/mix
@@ -0,0 +1,2 @@
+#!/bin/sh -eu
+HOME=$HOME/.config && $(which mix --all | grep --invert-match "local/bin" | head -n 1) "$@";
diff --git a/.local/bin/wrappers/mocp b/.local/bin/wrappers/mocp
new file mode 100755
index 0000000..8828b25
--- /dev/null
+++ b/.local/bin/wrappers/mocp
@@ -0,0 +1,2 @@
+#!/bin/sh -eu
+$(which mocp --all | grep --invert-match "local/bin" | head --lines 1) -M "$HOME/.config/moc" "$@"
diff --git a/.local/bin/wrappers/nix-index b/.local/bin/wrappers/nix-index
new file mode 100755
index 0000000..96c3990
--- /dev/null
+++ b/.local/bin/wrappers/nix-index
@@ -0,0 +1,16 @@
+#!/bin/sh -eu
+
+# https://github.com/nix-community/nix-index-database?tab=readme-ov-file#ad-hoc-download
+
+Update() {
+ directory=~/.local/share/nix-index
+ file="index-$(uname -m | sed 's/^arm64$/aarch64/')-$(uname | tr '[:lower:]' '[:upper:]')"
+ mkdir --parents $directory && cd $directory
+ wget --quiet --timestamping "https://github.com/Mic92/nix-index-database/releases/latest/download/$file"
+ printf "\nUpdating database %s\n\n" "$file"
+ ln --symbolic --force "$file" files
+}
+
+Update
+
+$(which nix-index --all | grep --invert-match "local/bin" | head --lines 1) "$@"
diff --git a/.local/bin/wrappers/sbcl b/.local/bin/wrappers/sbcl
new file mode 100755
index 0000000..b5ba6ea
--- /dev/null
+++ b/.local/bin/wrappers/sbcl
@@ -0,0 +1,2 @@
+#!/bin/sh -eu
+HOME=$HOME/.config && $(which sbcl --all | grep --invert-match "local/bin" | head -n 1) "$@";
diff --git a/.local/bin/wrappers/stalonetray b/.local/bin/wrappers/stalonetray
new file mode 100755
index 0000000..bcdf87c
--- /dev/null
+++ b/.local/bin/wrappers/stalonetray
@@ -0,0 +1,6 @@
+#!/bin/sh -eu
+$(which stalonetray --all | grep --invert-match "local/bin" | head -n 1) \
+ --background '#000000' \
+ --window-type normal \
+ --window-strut none \
+ "$@";
diff --git a/.local/bin/wrappers/swc b/.local/bin/wrappers/swc
new file mode 100755
index 0000000..c352781
--- /dev/null
+++ b/.local/bin/wrappers/swc
@@ -0,0 +1,2 @@
+#!/bin/sh -eu
+$(which swc --all | grep --invert-match "local/bin" | head --lines 1) "$@" --config-file "$HOME/.config/swc/swc.json"
diff --git a/.local/bin/wrappers/tidy b/.local/bin/wrappers/tidy
deleted file mode 100755
index d3a0aa4..0000000
--- a/.local/bin/wrappers/tidy
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/bin/sh -eu
-$(which tidy --all | grep --invert-match "local/bin" | head -n 1) \
- -config "$HOME/.config/tidy/default.conf" "$@"