aboutsummaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authorEuAndreh <eu@euandre.org>2022-05-12 12:01:54 -0300
committerEuAndreh <eu@euandre.org>2022-05-12 12:01:54 -0300
commit90eaebabcaaea74237f34cf05709625345f276cc (patch)
tree349e7609d20ecfb6567652a7e28595cec9647eb0 /bin
parent.usr/etc/i3/config: WIP setup extra bindings (diff)
downloaddotfiles-90eaebabcaaea74237f34cf05709625345f276cc.tar.gz
dotfiles-90eaebabcaaea74237f34cf05709625345f276cc.tar.xz
Move Git repository into ~/.usr/.git/
Diffstat (limited to 'bin')
-rwxr-xr-xbin/backup128
-rwxr-xr-xbin/brightness13
-rwxr-xr-xbin/clamp74
-rwxr-xr-xbin/color204
-rwxr-xr-xbin/copy67
-rwxr-xr-xbin/e79
-rwxr-xr-xbin/email74
-rwxr-xr-xbin/gc105
-rwxr-xr-xbin/grun102
-rwxr-xr-xbin/max61
-rwxr-xr-xbin/min61
-rwxr-xr-xbin/mkdtemp24
-rwxr-xr-xbin/mkstemp24
-rwxr-xr-xbin/msg90
-rwxr-xr-xbin/open101
-rwxr-xr-xbin/pre72
-rwxr-xr-xbin/print151
-rwxr-xr-xbin/qr58
-rwxr-xr-xbin/rfc117
-rwxr-xr-xbin/tmpname52
-rwxr-xr-xbin/uuid53
-rwxr-xr-xbin/with-email84
-rwxr-xr-xbin/without-env76
l---------bin/xdg-open1
-rwxr-xr-xbin/xmpp89
25 files changed, 1960 insertions, 0 deletions
diff --git a/bin/backup b/bin/backup
new file mode 100755
index 0000000..cab37dc
--- /dev/null
+++ b/bin/backup
@@ -0,0 +1,128 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ backup [-v] [-C COMMENT] ARCHIVE_TAG
+ backup -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+
+ Options:
+ -v enable verbose mode, useful for
+ interactive sessions
+ -C COMMENT the comment text to be attached to the archive
+ -h, --help show this message
+
+ ARCHIVE_TAG the tag used to create the new
+ backup (default: "cronjob")
+
+
+ The repository is expected to have been created with:
+
+ $ borg init -e repokey-blake2
+
+ The following environment variables are expected to be exported:
+
+ $BORG_PASSCOMMAND
+ $BORG_REPO
+ $BORG_REMOTE_PATH
+
+ Password-less SSH access is required, usually done via adding
+ ~/.ssh/id_rsa.pub to suyin:.ssh/authorized_keys.
+
+
+ Examples:
+
+ Run backup from cronjob:
+
+ $ backup cronjob
+
+ Create backup with comment, and verbose mode active:
+
+ $ backup -vC 'The backup has a comment' my-backup
+ EOF
+}
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+VERBOSE_FLAGS=''
+COMMENT=''
+while getopts 'vC:h' flag; do
+ case "$flag" in
+ v)
+ VERBOSE_FLAGS='--verbose --progress'
+ ;;
+ C)
+ COMMENT="$OPTARG"
+ ;;
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+assert_arg() {
+ if [ -z "$1" ]; then
+ printf 'Missing %s\n' "$2" >&2
+ usage >&2
+ exit 2
+ fi
+}
+
+ARCHIVE_TAG="${1:-}"
+assert_arg "$ARCHIVE_TAG" 'ARCHIVE_TAG'
+
+
+finish() {
+ STATUS=$?
+ printf '\n>>>\n>>> exit status: %s\n>>>\n\n' "$STATUS" >&2
+}
+trap finish EXIT
+
+run() {
+ borg create \
+ $VERBOSE_FLAGS \
+ --comment "$COMMENT" \
+ --exclude ~/.cache/ \
+ --exclude ~/Downloads/ \
+ --stats \
+ --compression lzma,9 \
+ "::{hostname}-{now}-$ARCHIVE_TAG" \
+ ~/
+ STATUS=$?
+
+ if [ "$STATUS" = 0 ] || [ "$STATUS" = 1 ]; then
+ echo 'WARNING, but no ERROR.' >&2
+ return 0
+ else
+ return "$STATUS"
+ fi
+}
+
+run || exit $?
diff --git a/bin/brightness b/bin/brightness
new file mode 100755
index 0000000..8e178ad
--- /dev/null
+++ b/bin/brightness
@@ -0,0 +1,13 @@
+#!/bin/sh
+set -eu
+
+BRIGHTNESS_DIFF="$1"
+
+DEVICE="$(cat "$XDG_CONFIG_HOME"/backlight-device)"
+HANDLER="/sys/class/backlight/$DEVICE"
+
+OLD_BRIGHTNESS="$(cat "$HANDLER"/brightness)"
+MAX_BRIGHTNESS="$(cat "$HANDLER"/max_brightness)"
+SUM=$((OLD_BRIGHTNESS + BRIGHTNESS_DIFF))
+NEW_BRIGHTNESS="$(clamp -- "$SUM" 0 "$MAX_BRIGHTNESS")"
+echo "$NEW_BRIGHTNESS" > "$HANDLER"/brightness || sudo chmod 666 "$HANDLER/brightness"
diff --git a/bin/clamp b/bin/clamp
new file mode 100755
index 0000000..a40b823
--- /dev/null
+++ b/bin/clamp
@@ -0,0 +1,74 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ clamp NUMBER MIN MAX
+ clamp -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -h, --help show this message
+
+ Clamp the NUMBER between MIN and MAX.
+ EOF
+}
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+while getopts 'h' flag; do
+ case "$flag" in
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+assert_arg() {
+ if [ -z "$1" ]; then
+ printf 'Missing %s\n\n' "$2" >&2
+ usage >&2
+ exit 2
+ fi
+}
+
+NUMBER="${1:-}"
+MIN="${2:-}"
+MAX="${3:-}"
+
+assert_arg "$NUMBER" 'NUMBER'
+assert_arg "$MIN" 'MIN'
+assert_arg "$MAX" 'MAX'
+
+
+if [ "$MIN" -gt "$MAX" ]; then
+ printf 'MIN (%s) is greater then MAX (%s).\n' "$MIN" "$MAX" >&2
+ exit 2
+fi
+
+min -- "$(max -- "$NUMBER" "$MIN")" "$MAX"
diff --git a/bin/color b/bin/color
new file mode 100755
index 0000000..0597278
--- /dev/null
+++ b/bin/color
@@ -0,0 +1,204 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ color -c COLOR TEXT
+ color -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+
+ Options:
+ -c COLOR
+ -h, --help show this message
+
+
+ The available colors are:
+ EOF
+ list_colors | sed 's/^/ /'
+}
+
+
+END="\033[0m"
+
+black() {
+ BLACK="\033[0;30m"
+ printf "${BLACK}${1}${END}"
+}
+
+blackb() {
+ BLACK_B="\033[1;30m"
+ printf "${BLACK_B}${1}${END}"
+}
+
+blacki() {
+ BLACK_I="\033[0;90m"
+ printf "${BLACK_I}${1}${END}"
+}
+
+white() {
+ WHITE="\033[0;37m"
+ printf "${WHITE}${1}${END}"
+}
+
+whiteb() {
+ WHITE_B="\033[1;37m"
+ printf "${WHITE_B}${1}${END}"
+}
+
+red() {
+ RED="\033[0;31m"
+ printf "${RED}${1}${END}"
+}
+
+redb() {
+ RED_B="\033[1;31m"
+ printf "${RED_B}${1}${END}"
+}
+
+green() {
+ GREEN="\033[0;32m"
+ printf "${GREEN}${1}${END}"
+}
+
+greenb() {
+ GREEN_B="\033[1;32m"
+ printf "${GREEN_B}${1}${END}"
+}
+
+yellow() {
+ YELLOW="\033[0;33m"
+ printf "${YELLOW}${1}${END}"
+}
+
+yellowb() {
+ YELLOW_B="\033[1;33m"
+ printf "${YELLOW_B}${1}${END}"
+}
+
+blue() {
+ BLUE="\033[0;34m"
+ printf "${BLUE}${1}${END}"
+}
+
+blueb() {
+ BLUE_B="\033[1;34m"
+ printf "${BLUE_B}${1}${END}"
+}
+
+bluei() {
+ BLUE_I="\033[0;94m"
+ printf "${BLUE_I}${1}${END}"
+}
+
+purple() {
+ PURPLE="\033[0;35m"
+ printf "${PURPLE}${1}${END}"
+}
+
+
+purpleb() {
+ PURPLE_B="\033[1;35m"
+ printf "${PURPLE_B}${1}${END}"
+}
+
+lightblue() {
+ LIGHT_BLUE="\033[0;36m"
+ printf "${LIGHT_BLUE}${1}${END}"
+}
+
+lightblueb() {
+ LIGHT_BLUE_B="\033[1;36m"
+ printf "${LIGHT_BLUE_B}${1}${END}"
+}
+
+COLOR_LIST='
+black
+blackb
+white
+whiteb
+red
+redb
+green
+greenb
+yellow
+yellowb
+blue
+blueb
+purple
+purpleb
+lightblue
+lightblueb
+blacki
+bluei
+'
+list_colors() {
+ for c in $COLOR_LIST; do
+ printf '%s\n' "$("$c" "$c")"
+ done
+}
+
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+while getopts 'c:h' flag; do
+ case "$flag" in
+ c)
+ EXISTS=false
+ for c in $COLOR_LIST; do
+ if [ "$OPTARG" = "$c" ]; then
+ EXISTS=true
+ break
+ fi
+ done
+ if [ "$EXISTS" = false ]; then
+ printf 'Invalid color: %s\n' "$OPTARG" >&2
+ exit 2
+ fi
+ COLOR_FN="$OPTARG"
+ ;;
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+assert_arg() {
+ if [ -z "$1" ]; then
+ printf 'Missing %s\n\n' "$2" >&2
+ usage >&2
+ exit 2
+ fi
+}
+
+TEXT="${1:-}"
+assert_arg "$COLOR_FN" '-c COLOR'
+assert_arg "$TEXT" 'TEXT'
+
+
+"$COLOR_FN" "$TEXT"
diff --git a/bin/copy b/bin/copy
new file mode 100755
index 0000000..64e1e32
--- /dev/null
+++ b/bin/copy
@@ -0,0 +1,67 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ copy [-n] < STDIN
+ copy -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -n remove newlines
+ -h, --help show this message
+
+ Examples:
+
+ Copy numbers to clipboard:
+ seq 10 | copy
+
+ Copy string without newline:
+ echo 'with automatic newline' | copy -n
+ EOF
+}
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+TRIM=false
+while getopts 'nh' flag; do
+ case "$flag" in
+ n)
+ TRIM=true
+ ;;
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+if [ "$TRIM" = true ]; then
+ cat - | tr -d '\n' | xclip -sel clip
+else
+ cat - | xclip -sel clip
+fi
diff --git a/bin/e b/bin/e
new file mode 100755
index 0000000..76b4899
--- /dev/null
+++ b/bin/e
@@ -0,0 +1,79 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ e [FILE]
+ e -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -h, --help
+
+
+ Flexibly run a text editor, either directly on in a pipe.
+
+ Examples:
+
+ Edit "file.txt":
+
+ $ e file.txt
+
+ Manipulate the content of a pipe midway:
+
+ $ seq 10 | e | grep 5
+
+ The editor used is either $VISUAL or $EDITOR, with a fallback to
+ vi in case any of those variables aren't defined.
+ EOF
+}
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+while getopts 'h' flag; do
+ case "$flag" in
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+
+CMD="${VISUAL:-${EDITOR:-vi}}"
+
+if [ ! -t 0 ]; then
+ F="$(mkstemp)"
+ cat > "$F"
+ exec 0</dev/tty
+ exec 3>&1
+ exec 1>/dev/tty
+ $CMD "$F"
+ cat "$F" >&3
+else
+ $CMD "$@"
+fi
diff --git a/bin/email b/bin/email
new file mode 100755
index 0000000..0a1fd15
--- /dev/null
+++ b/bin/email
@@ -0,0 +1,74 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ mail -s SUBJECT ADDRESS... < BODY
+ mail -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -s SUBJECT the email subject
+ -h, --help show this message
+
+ ADDRESS the email addresses to send the email to
+ BODY the text to be sent as the body
+ EOF
+}
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+while getopts 's:h' flag; do
+ case "$flag" in
+ s)
+ SUBJECT="$OPTARG"
+ ;;
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+assert_arg() {
+ if [ -z "$1" ]; then
+ {
+ printf 'Missing %s.\n' "$2"
+ printf '\n'
+ usage
+ } >&2
+ exit 2
+ fi
+}
+
+assert_arg "${SUBJECT:-}" '-s SUBJECT'
+assert_arg "${1:-}" 'ADDRESS'
+
+printf 'Subject: %s\n\n%s' \
+ "$(echo "$SUBJECT" | tr -d '\n')" \
+ "$(cat)" |
+ msmtpq -a euandreh "$@"
diff --git a/bin/gc b/bin/gc
new file mode 100755
index 0000000..ec6a8f1
--- /dev/null
+++ b/bin/gc
@@ -0,0 +1,105 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ gc
+ gc -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -h, --help show this message
+ EOF
+}
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+while getopts 'h' flag; do
+ case "$flag" in
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+
+disk() {
+ df -h . |
+ awk 'NR == 2 { printf "%s - %s/%s\n", $4, $3, $2 }'
+}
+
+
+gc_guix() {
+ pass show velhinho/0-andreh-password | sudo -S guix system delete-generations
+ guix home delete-generations
+ guix gc -d
+}
+
+gc_nohup() {
+ find ~/ -type f -name 'nohup.out' -exec rm -vf "{}" \;
+}
+
+gc_trash() {
+ yes | trash-empty
+}
+
+gc_tmpdir() {
+ find /tmp/ -type f -atime +10 -exec rm -vf "{}" \; ||:
+}
+
+gc_docker() {
+ if command -v docker; then
+ yes | docker system prune -a
+ docker rmi "$(docker images -a -q)" ||:
+ docker rm "$(docker ps -a -f status=exited -q)" ||:
+ docker stop "$(docker ps -a -q)" ||:
+ docker rm "$(docker ps -a -q)" ||:
+ yes | docker volume prune
+ yes | docker container prune
+ fi
+}
+
+gc_email() {
+ notmuch search --output=files --exclude=false tag:killed |
+ xargs -I{} rm -vf "{}"
+}
+
+
+BEFORE="$(disk)"
+gc_guix
+gc_nohup
+gc_trash
+gc_tmpdir
+gc_docker
+gc_email
+wait
+AFTER="$(disk)"
+
+printf 'Disk space:\n'
+printf ' before: %s\n' "$BEFORE"
+printf ' after: %s\n' "$AFTER"
diff --git a/bin/grun b/bin/grun
new file mode 100755
index 0000000..5884e9e
--- /dev/null
+++ b/bin/grun
@@ -0,0 +1,102 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ grun [-r RECIPIENT] FILENAME -- COMMAND...
+ grun -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -r RECIPIENT the recipient to encrypt to. Can be given
+ multiple for multiple recipients.
+ -h, --help show this message
+
+ COMMAND A command to be executed, that accepts input
+ in STDIN and emits result in STDOUT, and emits
+ errors as non-zero return codes.
+ FILENAME The GPG-encrypted file to be processed. If it
+ doesn't exist yet, it will be created.
+
+ Examples:
+
+ Edit "secrets.txt.gpg" using `vipe` and the default recipient:
+
+ $ grun secrets.txt.gpg -- vipe
+
+ Delete lines containing "FIXME" in todos.gpg:
+
+ $ grun -r ABC123DEF321 todos.gpg -- sed '/FIXME/d'
+
+ If COMMAND emits a non-zero return code, the file is left
+ unmodified.
+ EOF
+}
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+while getopts 'rh' flag; do
+ case "$flag" in
+ r)
+ RECIPIENTS_FLAG="${RECIPIENTS_FLAG:-} -r $OPTARG"
+ ;;
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+assert_arg() {
+ if [ -z "$1" ]; then
+ printf 'Missing %s\n\n' "$2" >&2
+ usage >&2
+ exit 2
+ fi
+}
+
+FILENAME="${1:-}"
+assert_arg "$FILENAME" 'FILENAME'
+shift
+
+if [ "${1:-}" != '--' ]; then
+ printf 'Missing "--" separator\n\n' >&2
+ usage >&2
+ exit 2
+fi
+shift
+
+assert_arg "${1:-}" 'COMMAND'
+
+
+if [ ! -e "$FILENAME" ]; then
+ OUT="$(printf '' | "$@")"
+else
+ OUT="$(gpg -dq "$FILENAME" | "$@")"
+fi
+
+echo "$OUT" | gpg -e ${RECIPIENTS_FLAG:--r eu@euandre.org} | sponge "$FILENAME"
diff --git a/bin/max b/bin/max
new file mode 100755
index 0000000..84585ee
--- /dev/null
+++ b/bin/max
@@ -0,0 +1,61 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ max NUMBER...
+ max -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -h, --help show this message
+
+ Get the maximum number from the given values.
+ EOF
+}
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+while getopts 'h' flag; do
+ case "$flag" in
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+if [ -z "${1:-}" ]; then
+ echo 0
+ exit
+fi
+
+N="$1"
+for n in "$@"; do
+ N=$((N > n ? N : n))
+done
+echo "$N"
diff --git a/bin/min b/bin/min
new file mode 100755
index 0000000..7868623
--- /dev/null
+++ b/bin/min
@@ -0,0 +1,61 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ min NUMBER...
+ min -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -h, --help show this message
+
+ Get the minimun number from the given values.
+ EOF
+}
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+while getopts 'h' flag; do
+ case "$flag" in
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+if [ -z "${1:-}" ]; then
+ echo 0
+ exit
+fi
+
+N="$1"
+for n in "$@"; do
+ N=$((N < n ? N : n))
+done
+echo "$N"
diff --git a/bin/mkdtemp b/bin/mkdtemp
new file mode 100755
index 0000000..9c7ddd8
--- /dev/null
+++ b/bin/mkdtemp
@@ -0,0 +1,24 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ mkstemp
+ mkstemp -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -h, --help show this message
+
+ Create a new temporary file and echo its name back.
+ EOF
+}
+
+name="$(tmpname)"
+mkdir "$name"
+echo "$name"
diff --git a/bin/mkstemp b/bin/mkstemp
new file mode 100755
index 0000000..ec92c14
--- /dev/null
+++ b/bin/mkstemp
@@ -0,0 +1,24 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ mkstemp
+ mkstemp -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -h, --help show this message
+
+ Create a new temporary file and echo its name back.
+ EOF
+}
+
+name="$(tmpname)"
+touch "$name"
+echo "$name"
diff --git a/bin/msg b/bin/msg
new file mode 100755
index 0000000..f3e893e
--- /dev/null
+++ b/bin/msg
@@ -0,0 +1,90 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ msg -X XMPP_MESSAGE -s -S SOUND_MESSAGE -m EMAIL -D DESKTOP -b
+ msg -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -X XMPP_MESSAGE send XMPP using the `xmpp` command
+ -s play ~/Desktop/medium.ogg sound
+ -S SOUND_MESSAGE say SOUND_MESSAGE using `speak`
+ -m EMAIL_SUBJECT send email with EMAIL_SUBJECT and empty body
+ -D DESKTOP_MESSAGE the desktop message for `notify-send`
+ -b print terminal bell
+ -h, --help show this message
+ EOF
+}
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+sound() {
+ play ~/Desktop/medium.ogg 2>/dev/null &
+}
+
+ACTION_DONE=false
+while getopts 'X:sS:m:D:bh' flag; do
+ case "$flag" in
+ X)
+ xmpp -m "$OPTARG" eu@euandreh.xyz
+ ACTION_DONE=true
+ ;;
+ s)
+ sound
+ ACTION_DONE=true
+ ;;
+ S)
+ echo "$OPTARG" | speak -v pt-BR
+ ACTION_DONE=true
+ ;;
+ m)
+ echo "" | email -s "$OPTARG" eu@euandre.org
+ ACTION_DONE=true
+ ;;
+ D)
+ notify-send "$OPTARG"
+ ACTION_DONE=true
+ ;;
+ b)
+ printf '\a'
+ ACTION_DONE=true
+ ;;
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+
+if [ "$ACTION_DONE" = false ]; then
+ sound
+ usage
+ help
+fi
+
+wait
diff --git a/bin/open b/bin/open
new file mode 100755
index 0000000..df1ad56
--- /dev/null
+++ b/bin/open
@@ -0,0 +1,101 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ open FILE...
+ open -h
+EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -h, --help show this message
+
+ Examples:
+
+ Open an HTML file on the current $BROWSER:
+ open index.html
+
+ Open multiple PDF files (with zathura):
+ open *.pdf
+EOF
+}
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+while getopts 'h' flag; do
+ case "$flag" in
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+if [ -z "${1:-}" ]; then
+ usage >&2
+ exit 2
+fi
+
+for f in "$@"; do
+ case "$f" in
+ *.ico|*.jpg|*.jpeg|*.png)
+ feh "$f"
+ ;;
+ https://www.youtube.com/watch*)
+ nohup mpv "$f" 1>&2 2>/dev/null &
+ ;;
+ *.flac|*.ogg|*.mkv|*.avi|*.mp4)
+ nohup mpv "$f" 1>&2 2>/dev/null &
+ ;;
+ http*|*.svg|*.html)
+ "$BROWSER" "$f"
+ ;;
+ gopher://*)
+ amfora "$f"
+ ;;
+ gemini://*)
+ telescope "$f"
+ ;;
+ *.pdf|*.djvu|*.ps|*.epub)
+ nohup zathura "$f" 1>&2 2>/dev/null &
+ ;;
+ *.txt)
+ less "$f"
+ ;;
+ *.midi)
+ timidity "$f"
+ ;;
+ mailto:*)
+ alot compose "$f"
+ ;;
+ *)
+ DIR="$(cd -- "$(dirname -- "$0")"; pwd)"
+ CMD="$(without-env PATH "$DIR" -- command -v xdg-open)"
+ "$CMD" "$f"
+ ;;
+ esac
+done
diff --git a/bin/pre b/bin/pre
new file mode 100755
index 0000000..233ac1a
--- /dev/null
+++ b/bin/pre
@@ -0,0 +1,72 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ pre [-c COLOR] PREFIX
+ pre -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -c COLOR ANSI color to be used on the prefix text
+ -h, --help show this message
+ EOF
+}
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+COLOR=''
+while getopts 'c:h' flag; do
+ case "$flag" in
+ c)
+ COLOR="$OPTARG"
+ ;;
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+assert_arg() {
+ if [ -z "$1" ]; then
+ printf 'Missing %s\n\n' "$2" >&2
+ usage >&2
+ exit 2
+ fi
+}
+
+PREFIX="${1:-}"
+assert_arg "$PREFIX" 'PREFIX'
+
+while read -r line; do
+ if [ -z "$COLOR" ]; then
+ printf '%s: %s\n' "$PREFIX" "$line"
+ else
+ printf '%s: %s\n' "$(color -c "$COLOR" "$PREFIX")" "$line"
+ fi
+done
diff --git a/bin/print b/bin/print
new file mode 100755
index 0000000..735d8ba
--- /dev/null
+++ b/bin/print
@@ -0,0 +1,151 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ print [-d] [-q QUALITY] [FILE...]
+ print -h
+EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -d print duplex/double-sided
+ -q QUALITY choose the print quality, either:
+ low, medium (default) or high.
+ -h, --help show this message
+
+ Examples:
+
+ Print the given PostScript file with default quality:
+ $ print f1.ps
+
+ Print multiple PDF files with high quality:
+ $ print -dq high *.pdf
+
+ Print the file from STDIN, double-sided:
+ $ print -d < f2.ps
+
+ Print multiple source code files:
+ $ print src/*.{c,h}
+EOF
+}
+
+mkdtemp() {
+ name="$(echo 'mkstemp(template)' |
+ m4 -D template="${TMPDIR:-/tmp}/m4-tmpname.")"
+ rm -f "$name"
+ mkdir "$name"
+ echo "$name"
+}
+
+n_pages() {
+ pdftk "$1" dump_data | awk '/NumberOfPages/ { print $2 }'
+}
+
+main() {
+ if file -b "$FILE" | grep -q PostScript; then
+ ps2pdf "$FILE" "$NEWDIR"/in.pdf
+ elif file -b "$FILE" | grep -q PDF; then
+ cp "$FILE" "$NEWDIR"/in.pdf
+ else
+ enscript -o- "$FILE" | ps2pdf - "$NEWDIR"/in.pdf
+ fi
+ cd "$NEWDIR"
+
+ if [ -z "$DUPLEX" ]; then
+ lp in.pdf
+ cd - > /dev/null
+ return
+ fi
+
+ if [ "$(n_pages in.pdf)" = '1' ]; then
+ lp in.pdf
+ return
+ fi
+
+ pdftk A=in.pdf cat Aodd output odd.pdf
+ pdftk A=in.pdf cat Aeven output even.pdf
+
+ NODD="$(n_pages odd.pdf)"
+ NEVEN="$(n_pages even.pdf)"
+
+ printf 'Printing odd pages...\n' >&2
+ lp odd.pdf
+ printf 'Has printing finished yet? Once it does, reload the pages and hit it enter to continue. '
+ read -r < /dev/tty
+ lp even.pdf
+
+ if [ "$NODD" != "$NEVEN" ]; then
+ printf '\n' | lp
+ fi
+
+ cd - > /dev/null
+}
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+lpoptions -o PrintQuality=standard
+DUPLEX=
+while getopts 'dq:h' flag; do
+ case "$flag" in
+ d)
+ DUPLEX=1
+ ;;
+ q)
+ case "$OPTARG" in
+ low)
+ lpoptions -o PrintQuality=draft
+ ;;
+ medium)
+ lpoptions -o PrintQuality=standard
+ ;;
+ high)
+ lpoptions -o PrintQuality=high
+ ;;
+ *)
+ echo "Bad QUALITY option: \"$OPTARG\"" >&2
+ exit 2
+ ;;
+ esac
+ ;;
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+NEWDIR="$(mkdtemp)"
+if [ -z "${1:-}" ]; then
+ FILE="$NEWDIR"/STDIN
+ cat - > "$FILE"
+ main
+else
+ for f in "$@"; do
+ FILE="$f"
+ main
+ done
+fi
diff --git a/bin/qr b/bin/qr
new file mode 100755
index 0000000..4821a46
--- /dev/null
+++ b/bin/qr
@@ -0,0 +1,58 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ qr [-s PIXEL_SIZE]
+ qr -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -s PIXEL_SIZE size of the pixel (default 10)
+ -h, --help show this help message
+
+ Read data from STDIN and present a QR image with said data.
+ EOF
+}
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+PIXEL_SIZE=10
+while getopts 's:h' flag; do
+ case "$flag" in
+ s)
+ PIXEL_SIZE="$OPTARG"
+ ;;
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+
+cat | qrencode -s "$PIXEL_SIZE" -o- | feh -
diff --git a/bin/rfc b/bin/rfc
new file mode 100755
index 0000000..a483595
--- /dev/null
+++ b/bin/rfc
@@ -0,0 +1,117 @@
+#!/bin/sh
+set -eu
+
+TARBALL_URL='https://www.rfc-editor.org/in-notes/tar/RFC-all.tar.gz'
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ rfc [-w] RFC_NUMBER
+ rfc -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -w Show the path to the RFC file instead of displaying
+ its contents.
+ -h, --help show this message
+
+ Lookup the given RFC
+ in $XDG_DATA_HOME/doc/rfc/ (defaults to ~/.local/share),
+ and feed it into the $PAGER, akin to doing:
+
+ $ $PAGER $XDG_DATA_HOME/doc/rfc/rfc$RFC_NUMBER.txt
+
+ If the $XDG_DATA_HOME/doc/rfc/ directory doesn't exist, it tries to
+ create it by downloading the latest RFC tarball [0] and placing all .txt
+ files there.
+ EOF
+
+ printf '\n[0]: %s\n' "$TARBALL_URL"
+}
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+while getopts 'wh' flag; do
+ case "$flag" in
+ w)
+ WHERE=true
+ ;;
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+RFC_NUMBER="${1:-}"
+if [ -z "$RFC_NUMBER" ]; then
+ echo 'Missing argument RFC_NUMBER' >&2
+ usage >&2
+ exit 2
+fi
+
+D="${XDG_DATA_HOME:-$HOME/.local/share}/doc/rfc"
+if [ ! -e "$D" ]; then
+ printf 'RFC directory does not exist:\n\t%s/\n\n' "$D"
+ printf 'Do you want to download the files to create it? [Y/n] '
+ read -r yesno
+ if [ "$yesno" != 'n' ] && [ "$yesno" != 'N' ]; then
+ CACHE_DIR="${XDG_CACHE_HOME:-$HOME/.cache}/rfc-cli"
+ mkdir -p "$CACHE_DIR"
+ wget -cO "$CACHE_DIR"/RFC-all.tar.gz "$TARBALL_URL"
+ rm -rf "$CACHE_DIR/tmp"
+ mkdir -p "$CACHE_DIR/tmp"
+ tar \
+ -C "$CACHE_DIR/tmp" \
+ -xvf "$CACHE_DIR"/RFC-all.tar.gz \
+ --wildcards \
+ 'rfc*.txt'
+ mkdir -p "$(dirname "$D")"
+ mv "$CACHE_DIR/tmp" "$D"
+ fi
+fi
+
+F="$D/rfc${RFC_NUMBER}.txt"
+if [ ! -e "$F" ]; then
+ printf 'Given RFC_NUMBER "%s" does not exist at:\n%s\n' \
+ "$RFC_NUMBER" "$F" >&2
+ exit 2
+fi
+
+if [ "${WHERE:-}" = true ]; then
+ printf '%s\n' "$F"
+ exit
+fi
+
+view() {
+ if [ -t 1 ]; then
+ ${PAGER:-cat}
+ else
+ cat
+ fi
+}
+
+view < "$F"
diff --git a/bin/tmpname b/bin/tmpname
new file mode 100755
index 0000000..d83fc87
--- /dev/null
+++ b/bin/tmpname
@@ -0,0 +1,52 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ tmpname
+ tmpname -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -h, --help show this message
+
+ Generate a temporary name.
+ EOF
+}
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+while getopts 'h' flag; do
+ case "$flag" in
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+echo "${TMPDIR:-/tmp}/uuid-tmpname with spaces.$(uuid)"
diff --git a/bin/uuid b/bin/uuid
new file mode 100755
index 0000000..74d0fba
--- /dev/null
+++ b/bin/uuid
@@ -0,0 +1,53 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ uuid
+ uuid -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -h, --help show this message
+
+ Generate UUID from /dev/random.
+ EOF
+}
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+while getopts 'h' flag; do
+ case "$flag" in
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+od -xN20 /dev/random |
+ awk 'NR == 1 { OFS="-"; print $2$3,$4,$5,$6,$7$8$9; exit }'
diff --git a/bin/with-email b/bin/with-email
new file mode 100755
index 0000000..9d6326b
--- /dev/null
+++ b/bin/with-email
@@ -0,0 +1,84 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ with-email [-s SUBJECT] COMMAND...
+ with-email -h
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -s SUBJECT set the subject of the email
+ -h, --help show this message
+
+ COMMAND the command to be wrapped
+ EOF
+}
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+SUBJECT='NO SUBJECT'
+while getopts 's:h' flag; do
+ case "$flag" in
+ s)
+ SUBJECT="$OPTARG"
+ ;;
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+assert_arg() {
+ if [ -z "$1" ]; then
+ printf 'Missing %s\n\n' "$2" >&2
+ usage >&2
+ exit 2
+ fi
+}
+
+assert_arg "${1:-}" 'COMMAND...'
+
+now() {
+ date '+%Y-%m-%dT%H:%M:%S%Z'
+}
+
+OUT="$(mkstemp)"
+{
+ printf 'Running command: %s\n' "$*"
+ printf 'Starting at: %s\n' "$(now)"
+ printf '\n'
+
+ STATUS=0
+ "$@" 2>&1 || STATUS=$?
+
+ printf '\n'
+ printf '\nFinished at: %s\n' "$(now)"
+} 2>&1 1>"$OUT"
+
+email -s "(exit status: $STATUS) - $SUBJECT" eu@euandre.org < "$OUT"
diff --git a/bin/without-env b/bin/without-env
new file mode 100755
index 0000000..45c41ca
--- /dev/null
+++ b/bin/without-env
@@ -0,0 +1,76 @@
+#!/bin/sh
+set -eu
+
+usage() {
+ cat <<-'EOF'
+ Usage:
+ without-env ENVVAR PATH -- COMMAND...
+ without-env [-h]
+ EOF
+}
+
+help() {
+ cat <<-'EOF'
+
+ Options:
+ -h, --help show this message
+
+ Examples:
+
+ Execute "command -V" filtering ~/bin, to get where "w3m" is
+ in $PATH, other than ~/bin:
+ $ without-env PATH ~/bin -- command -v w3m
+
+ Compile foo.c, excluding ~/.local/include
+ from $C_INCLUDE_PATH:
+ $ without-env C_INCLUDE_PATH ~/.local/include -- cc -co foo.o foo.c
+ EOF
+}
+
+for flag in "$@"; do
+ case "$flag" in
+ --)
+ break
+ ;;
+ --help)
+ usage
+ help
+ exit
+ ;;
+ *)
+ ;;
+ esac
+done
+
+while getopts 'h' flag; do
+ case "$flag" in
+ h)
+ usage
+ help
+ exit
+ ;;
+ *)
+ usage >&2
+ exit 2
+ ;;
+ esac
+done
+shift $((OPTIND - 1))
+
+assert_arg() {
+ if [ -z "$1" ]; then
+ printf 'Missing %s\n' "$2" >&2
+ exit 2
+ fi
+}
+
+assert_arg "${1:-}" 'ENVVAR'
+assert_arg "${2:-}" 'PATH'
+assert_arg "${3:-}" '--'
+
+eval "export $1=\"\$(echo \"\$$1\" | sed \"s|\$2:||g\")\""
+shift # drop $1
+shift # drop $2
+shift # drop --
+
+"$@"
diff --git a/bin/xdg-open b/bin/xdg-open
new file mode 120000
index 0000000..ce4a72b
--- /dev/null
+++ b/bin/xdg-open
@@ -0,0 +1 @@
+open \ No newline at end of file
diff --git a/bin/xmpp b/bin/xmpp
new file mode 100755
index 0000000..472e9ca
--- /dev/null
+++ b/bin/xmpp
@@ -0,0 +1,89 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+import getopt
+import logging
+import slixmpp
+
+USAGE = """\
+Usage:
+ xmpp [-d] [-F FROM_JID] -m MESSAGE TO_JID...
+ xmpp -h"""
+
+HELP = """
+Options:
+ -d run in DEBUG mode
+ -m MESSAGE the text of the message to be sent
+ -h, --help show this message
+
+ FROM_JID the address used to send the message from
+ TO_JID the addresses where to send the message to"""
+
+class SendMsgBot(slixmpp.ClientXMPP):
+ def __init__(self, jid, password, on_start):
+ slixmpp.ClientXMPP.__init__(self, jid, password)
+
+ self.on_start = on_start
+ self.add_event_handler("session_start", self.start)
+
+ def start(self, event):
+ self.on_start(self)
+ self.disconnect(wait=True)
+
+def main():
+ logging.basicConfig(level=logging.INFO)
+ from_ = "bot@euandreh.xyz"
+ message = ""
+
+ for s in sys.argv:
+ if s == "--":
+ break
+ elif s == "--help":
+ print(USAGE)
+ print(HELP)
+ sys.exit()
+
+ try:
+ opts, args = getopt.getopt(sys.argv[1:], 'm:F:dh')
+ except getopt.GetoptError as err:
+ print(err, file=sys.stderr)
+ print(USAGE, file=sys.stderr)
+ sys.exit(2)
+ for o, a in opts:
+ if o == "-m":
+ message = a
+ elif o == "-F":
+ from_ = a
+ elif o == "-d":
+ logging.basicConfig(level=logging.DEBUG)
+ elif o == "-h":
+ print(USAGE)
+ print(HELP)
+ sys.exit()
+ else:
+ assert False, "unhandled option"
+
+ if message == "":
+ print("Missing -m MESSAGE", file=sys.stderr)
+ print(USAGE, file=sys.stderr)
+ sys.exit(2)
+
+ if args == []:
+ print("Missing TO_JID", file=sys.stderr)
+ print(USAGE, file=sys.stderr)
+ sys.exit(2)
+
+ passcmd = "pass show VPS/kuvira/XMPP/" + from_ + " | head -n1 | tr -d '\\n'"
+ password = os.popen(passcmd).read()
+
+ def on_start(self):
+ for to in args:
+ self.send_message(mto=to, mbody=message, mtype='chat')
+
+ xmpp = SendMsgBot(from_, password, on_start)
+ xmpp.connect()
+ xmpp.process(forever=False)
+
+if __name__ == "__main__":
+ main()