This is where my custom scripts live. That includes shell scripts, dmenu
scripts, and potentially programming-language-specific scripts. One of the
things I strive for is to have the same script available in different
environments. For example, I have a dmenu script for creating a QR code, which
allows me to scan or generate a QR code with a few keystrokes. But sometimes I
want to call this script from a terminal, so I also have a qr
command available
in ~/.local/bin
. Likewise, I have ideas to port some scripts to tridactyl (a
firefox extension) as well. Since those different versions of the same script
differ only by UX, it is useful to have all the versions defined in the same
place - here.
Note: For code reusability, I use some helper code blocks defined at the bottom
of this document. These code blocks are utilized via org-sbe
or via noweb references.
git() {
alternative-command git "$@"
}
git_root="$(git root 2>/dev/null)"
if [ "$git_root" = ~ ]; then
if [ "$PWD" = ~ ] || [[ "${PWD#~/}" =~ ^\. ]]; then
git "$@"
else
export GIT_CEILING_DIRECTORIES=~
git "$@"
fi
else
git "$@"
fi
for arg in "$@"; do
[ ! -e "$arg" ] && continue
arg="$(realpath "$arg")"
if echo "$arg" \
| grep -q '^/home' && echo "$arg" \
| grep -qv '^/home/[^/]\+/[^/]\+'
then
echo "WTF: Trying to delete a home directory???"
exit 1
fi
done
/usr/bin/rm "$@"
This is a wrapper around GNU pass
.
shopt -s expand_aliases
# TODO: Use alternative-command instead
alias pass=/usr/bin/pass
clipctl disable >/dev/null || true
# Add a password read directly from stdin (without prompting to interactively
# enter it)
if [ "$1" = "add" ]; then
dest=~/.password-store/"$2"
destdir="$(dirname "$dest")"
mkdir -p "$destdir"
cat | gpg -e -o- -r "$(cat ~/.password-store/.gpg-id)" > "$dest".gpg
else
pass "$@"
fi
clipctl enable >/dev/null || true
This is a wrapper around passmenu
, trying to use passmenu-otp
if present,
otherwise falling back to the default passmenu
.
if command -v passmenu-otp >/dev/null; then
passmenu-otp "$@"
else
alternative-command passmenu "$@"
fi
Note: This depends on my custom build of dmenu with the height patch applied. The PKGFILE can be found here.
alternative-command dmenu -b -f -i -h 25 \
-nb '#1e1e1e' -nf '#dddddd' -sb '#99a3ff' -sf '#1e1e1e' \
-fn 'Ubuntu Mono-12' \
"$@"
/usr/bin/ipython "$@" -i -- "$PYTHONSTARTUP"
# Fake gvim which will just launch a terminal with neovim in it
for arg in "$@"; do
# Hacky solution that will open files in real gvim when opened by xdg-open
if [ "$arg" = "-f" ]; then /usr/bin/gvim "$@"; exit; fi
done
# nvim is launched through the shell because otherwise it behaves weirdly
# All arguments are surrounded with ''
FISH_CMD="$(printf "%q " nvim "$@")"
if [ ! -t 0 ]; then
# Input is from a PIPE
pipe_input="$(mktemp -t XXXXXXXXXXXXXXXX)"
cat > "$pipe_input"
FISH_CMD="cat $pipe_input | $FISH_CMD"
fi
alacritty --class Alacritty,Float -e fish -C "$FISH_CMD"
if [ ! -t 0 ]; then
# Input is from a PIPE
rm -f "$pipe_input"
fi
/usr/bin/redshift -l 43.84:18.35
/usr/bin/firefox $(printarg --new-tab "$@" | uniq)
# Run emacsclient in the terminal, unless the -c option is given, which will
# open the GUI.
# - Also enables proper color support.
# - Any additional arguments you provide are passed to emacsclient
export TERM='xterm-256color'
create_frame=""
socket_name=""
modified_options=()
print_help() {
emacsclient --help
echo
echo "CUSTOM OPTIONS:"
echo -e "--wait \t\t\tOverride --no-wait if it would otherwise be in effect"
echo -e "--no-tty \t\tUndoes the effects of --tty"
}
declare -A custom_options_map
# Get custom option named $1 (success code = on, error code = off)
# If $2 is given, then the option named $1 is set to the truth
# value of $2 (empty is false, non-empty is true)
custom_option() {
if [ "$#" = 2 ]; then
custom_options_map["$1"]="$2"
else
[ -n "${custom_options_map["$1"]}" ]
fi
}
# Since this is a wrapper around emacsclient, we need to parse and pass through
# all options that emacsclient recognizes
parsed_args="$(
getopt --name "$BASH_SOURCE" \
--options "h"VHtcrF:enw:qud:s:f:a:T: \
--long "wait,no-tty",version,help,tty,create-frame,reuse-frame,frame-parameters:,eval,no-wait,timeout:,quiet,suppress-output,display:,parent-id:,socket-name:,server-file:,alternate-editor:,tramp: \
-- "$@"
)"
eval set -- "$parsed_args"
while :; do
case "$1" in
-h | --help)
print_help
exit ;;
-c | --create-frame)
create_frame="true"
modified_options+=("$1")
shift 1 ;;
-s | --socket-name)
modified_options+=("$1" "$2")
socket_name="$2"
shift 2 ;;
--no-wait)
custom_option wait ""
shift 1 ;;
--wait)
custom_option wait "true"
shift 1 ;;
-t | --tty)
custom_option tty "true"
shift 1 ;;
--no-tty)
custom_option tty ""
shift 1 ;;
--)
shift 1
break ;;
*)
modified_options+=("$1")
shift 1 ;;
esac
done
if [ ! -t 0 ]; then
custom_option wait ""
fi
# Set reasonable default values for --tty option and --no-wait option
# based on --create-frame.
if [ ! -v custom_options_map["tty"] ]; then
custom_option tty "$([ -z "$create_frame" ] && echo true)"
fi
if [ ! -v custom_options_map["wait"] ]; then
custom_option wait "$([ -z "$create_frame" ] && echo true)"
fi
if custom_option tty; then
modified_options+=(--tty)
fi
if ! custom_option wait; then
modified_options+=(--no-wait)
fi
if [ -z "$socket_name" ]; then
modified_options+=(--socket-name emacs)
fi
emacsclient() {
exec -a "$BASH_SOURCE" command emacsclient "$@"
}
set -- "${modified_options[@]}" -- "$@"
if [ ! -t 0 ]; then
file="$(mktemp /tmp/haris-pipe-XXXXXXXX)"
tee "$file" <&0 >/dev/null &
bg_pid="$!"
set -- "$@" "$file"
trap "kill $bg_pid &>/dev/null" EXIT INT ERR
fi
exec -a "$BASH_SOURCE" emacsclient \
--alternate-editor ~/.local/lib/haris/emacs-alternate-editor \
"$@"
if [ -n "$bg_pid" ]; then
wait "$bg_pid"
fi
is_running() {
emacsclient --no-wait --socket-name emacs --eval '(ignore)' >/dev/null 2>&1
}
id="$(notify-send --print-id "Emacs" "Starting daemon...")"
systemctl restart --user emacs
until is_running; do
sleep 2s
done
notify-send --print-id --replace-id="$id" "Emacs" "Daemon ready"
complete -c myemacs --wraps emacsclient
complete -c myemacs --long "wait" --description "Override --no-wait if it would otherwise be in effect"
myemacs -c --frame-parameters='(quote (name . "EmacsFloat"))' "$@"
export DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/<<eval-real-uid()>>/bus"
# If notify-send can't communicate, it blocks. That's the reason for the timeout
timeout 1s alternative-command notify-send "$@"
# Sudo but with a hook that sends a notification when the prompt is shown
# TODO: Not robust enough. What if the wrapped command itself accepts a --stdin flag?
if [ -z "$SUDO_ASKPASS" ] && [[ ! "$*" =~ --stdin ]] && [[ ! "$*" =~ -S ]]; then
export SUDO_ASKPASS="$(mktemp)"
chmod u+x "$SUDO_ASKPASS"
cat > "$SUDO_ASKPASS" <<'EOF'
<<sudo-askpass>>
EOF
trap "rm -f $SUDO_ASKPASS" EXIT
alternative-command sudo --askpass "$@"
else
alternative-command sudo "$@"
fi
#!/usr/bin/env sh
askpass "[sudo] password for $(getent passwd "$USER" | cut -d: -f1)" "SUDO password" "Please enter your password"
# This checks whether ssh is run via sshpass or perhaps some other tool that
# assigns its own terminal to ssh.
is_tty_available() {
{ : >/dev/tty; } &>/dev/null
}
if [ -z "$SSH_ASKPASS" ] && is_tty_available; then
export SSH_ASKPASS="$(mktemp)"
export SSH_ASKPASS_REQUIRE="prefer"
chmod u+x "$SSH_ASKPASS"
cat > "$SSH_ASKPASS" <<'EOF'
<<ssh-askpass>>
EOF
trap "rm -f $SSH_ASKPASS" EXIT
fi
alternative-command ssh "$@"
# The default prompt supplied as call args
prompt="$(echo "$1" | sed 's/: $//')"
askpass "$prompt" "SSH passphrase" "Please enter SSH passphrase"
alternative-command alacritty "$@"
alacritty --class Alacritty,Float "$@"
export XDG_CURRENT_DESKTOP=Sway
/usr/bin/sway --unsupported-gpu "$@"
pkill gpg-agent 2>/dev/null
alternative-command xlock "$@"
The name code
is a bit too generic. I need something more specific in order to
get better search matches.
code "$@"
export TERM=xterm-256color
# alternative-command lnav "$@"
/usr/bin/lnav "$@"
alternative-command clipmenu "$@"
set -e
if [ "$#" -ne 1 ]; then
echo "Usage: $0 enable|disable|status|tmp-disable"
echo " enable: enable clipboard synchronization"
echo " disable: disable clipboard synchronization"
echo " status: shows the current clipboard synchronization status"
echo " tmp-disable: disable clipboard synchronization for the current shell pipeline, cat stdin to stdout"
exit 1
fi
status_file="$XDG_RUNTIME_DIR/haris/clipctl-status"
mkdir -p "$(dirname "$status_file")"
previous_state="$(cat "$status_file" 2>/dev/null || echo "enable")"
# Perform action
if [ "$1" = "enable" ] || [ "$1" = "disable" ]; then
alternative-command clipctl "$1"
echo "$1" > "$status_file"
elif [ "$1" = "status" ]; then
echo "$previous_state"
elif [ "$1" = "tmp-disable" ]; then
if [ "$previous_state" = "disable" ]; then
clipctl disable >/dev/null # Just making sure
cat
else
alternative-command clipctl disable >/dev/null &
wait
cat
alternative-command clipctl "$(cat "$status_file")" >/dev/null
fi
else
echo "Invalid argument: $1"
exit 1
fi
complete -c clipctl -f -a "enable" -d "Enable clipboard synchronization"
complete -c clipctl -f -a "disable" -d "Disable clipboard synchronization"
complete -c clipctl -f -a "status" -d "Show the current clipboard synchronization status"
complete -c clipctl -f -a "tmp-disable" -d "Disable clipboard synchronization for the current shell pipeline; cat"
if grep -q 'down.*--volumes' <<<"$*"; then
echo 'Option --volumes used. Make sure this is the intended compose project'
echo -e "COMPOSE_PROJECT_NAME: \e[1;31m $COMPOSE_PROJECT_NAME\e[0m"
read -p "Are you sure? [y|N]: " -n 1 answer
if [ "$answer" != "y" ]; then
echo
echo "Aborting."
exit 1
fi
fi
if [ "$(which -a docker-compose | uniq | wc -l)" -gt 1 ]; then
alternative-command docker-compose "$@"
else
docker compose "$@"
fi
export TERM=xterm-256color
alternative-command ngrok "$@"
flatpak_name="com.github.IsmaelMartinez.teams_for_linux"
if [ "$(which -a teams | uniq | wc -l)" -gt 1 ]; then
alternative-command teams "$@"
elif [ "$(which -a teams-for-linux | uniq | wc -l)" -gt 0 ]; then
teams-for-linux "$@"
elif flatpak info "$flatpak_name" >/dev/null 2>&1; then
flatpak run "$flatpak_name"
else
echo "Teams is not installed. Tried:" >&2
printf ' %s\n' teams teams-for-linux "Flatpak: $flatpak_name"
fi
if [ "$(which -a yarn | uniq | wc -l)" -gt 1 ]; then
alternative-command yarn "$@"
else
yarnpkg "$@"
fi
export PAGER=cat
alternative-command iredis "$@"
export QT_QPA_PLATFORMTHEME=qt
alternative-command VirtualBox "$@"
alternative-command screenkey --bg-color "#99a3ff" --font-color "#1e1e1e" "$@"
if xprop -id "$WINDOWID" | grep -q _NET_WM_STATE_FULLSCREEN; then
myemacs --wait "$@"
elif which myemacs-float >/dev/null; then
myemacs-float --wait "$@" >/dev/null
else
editor "$@"
fi
exists() {
which "$1" >/dev/null
}
if exists myemacs; then
myemacs "$@"
elif exists nvim; then
nvim "$@"
elif exists vim; then
vim "$@"
elif exists nvim; then
vi "$@"
fi
pushd "$(dirname "$1")" >/dev/null
echo "$PWD/$(basename "$1")"
popd >/dev/null
# Usage: askpass [PROMPT] [WINDOW_TITLE] [NOTIFICATION]
prompt="$1"
window_title="$2"
notification="$3"
if [ "$TERM" != "dumb" ]; then
if [ -n "$DISPLAY" ]; then
id="$(notify-send "$window_title" "$notification" --print-id --expire-time 0)"
trap "dunstify --close=$id" EXIT
fi
echo -n "$prompt: " >/dev/tty
stty -echo </dev/tty
head -1 </dev/tty | tr -d '\n'
echo >/dev/tty
else
{
echo SETTITLE "$window_title"
echo SETDESC "$prompt"
echo SETPROMPT Password:
echo GETPIN
echo BYE
} | pinentry -g 2>&1 | sed -n "/^D/ s/^D //p" | tr -d '\n'
fi
TERM=dumb askpass "$@"
chmod u+x "$@"
# Compare the contents of two GPG encrypted files
diff <(gpg --decrypt --output - "$1") <(gpg --decrypt --output - "$2") "${@:3}"
# Diff two files named $1 and $2, piped through a command given as the remaining
# arguments.
options=()
for arg in "$@"; do
if [[ "$arg" =~ ^- ]]; then
options=("${options[@]}" "$arg")
shift
else
break
fi
done
file1="$1"
file2="$2"
shift 2
tmp1="$(mktemp)"
tmp2="$(mktemp)"
cat "$file1" | "$@" > "$tmp1"
cat "$file2" | "$@" > "$tmp2"
diff --color=auto "${options[@]}" "$tmp1" "$tmp2"
rm -f "$tmp1" "$tmp2"
diffpiped --strip-trailing-cr <(echo -e "a\nb") <(echo -e "a\nB") sed 's/a/A/g'
type "$@" 2>/dev/null | awk '{print $3}'
exit "${PIPESTATUS[0]}"
cat $(fcmd cf) | sed -n 's/\s*\(.*\)).*/\1/p'
lookup_dirs=(
~/.local/share/wallpapers
~/.local/share/backgrounds
/usr/local/share/wallpapers
/usr/local/share/backgrounds
/usr/share/wallpapers
/usr/share/backgrounds
)
fd . "${lookup_dirs[@]}" 2>/dev/null
# Print n of the received arguments, where n=$1
[ "$#" = 0 ] && exit 1
num="$1"
shift
printarg $(printarg "${@}" | head -"$num")
# Print commandline arguments passed to this function each on its own line
printf "%s\n" "$@"
# Remove whitespace from a file (or stdin if input is from a pipe) and write the
# output to stdout (or rewrite the file if the -i option is given)
[ ! -t 0 ] &&\
cat | sed 's:\s\+$::' ||\
sed 's:\s\+$::' "$@"
# Create an ad-hoc file, edit it in $EDITOR and then print its path. If a '-'
# argument is given, the file contents are printed instead of its path.
destdir="/tmp/adhoc-files"
mkdir -p "$destdir"
cd "$destdir"
if [ "$#" != 0 ] && [ "$1" = "-" ]; then
print_content=true
files=("${@:2}")
else
files=("$@")
fi
# Convert files to realpaths
readarray -t files < <(realpath "${files[@]}" 2>/dev/null)
if [ -z "$files" ]; then
files=("$(mktemp "$destdir"/XXXXXXXXXXX)")
fi
myemacs-float --wait "${files[@]}" >&2
if [ -n "$print_content" ]; then
cat "${files[@]}"
else
realpath "${files[@]}"
fi
if [ "$TERM" = "linux" ]; then
"$@"
exit
fi
"$@" && notify-send "Command $* exited successfully." || {
err=$?
notify-send -u critical "Command $* exited with error $err."
}
exit $err
complete --command cmd-with-notify -f -a '(complete -C(commandline -cp | sed "s:\S\+::"))'
[ "$#" != 1 ] && exit 1
ln -sf .xinitrc-"$1" ~/.xinitrc
complete -c xpreset -f -a "(pushd ~; ls .xinitrc-* | string replace .xinitrc- ''; popd)"
n="$(xrandr --listmonitors | head -1 | awk '{print $NF}')"
[ "$n" = 1 ] && {\
xrandr2 --auto
MSG='enabled'
} || {\
xrandr2 --off
MSG='disabled'
}
[ "$?" = 0 ] && notify-send "second monitor sucessfully $MSG" || notify-send -u "monitor operation unsuccessful"
# Fix wallpaper on second monitor
feh --bg-fill .wallpaper
# Wrapper for xrandr command with some options applied based on my current
# monitor configuration
xrandr --output HDMI-1-0 "$@" --pos 1920x1080
# Create three tmux panes:
# - A SOURCE text file opened in vim
# - A SCRIPT file opened in vim
# - An output buffer that shows the results of processing SOURCE with SCRIPT
#
# The SOURCE is supplied to the SCRIPT via pipe, i.e. the output buffer shows
# the results of:
#
# SCRIPT < SOURCE
#
# The output automatically updates when one of SCRIPT, SOURCE changes.
#
# USAGE: pipetest [SOURCE] [SCRIPT]
#
# If the optional arguments SCRIPT and SOURCE are given, the SCRIPT and SOURCE
# buffers will have an initial content equal to the content of those files.
vim_executable=vim
if command -v nvim >/dev/null; then
vim_executable=nvim
fi
temp_dir="$(mktemp -d)"
INITIAL_SOURCE="$1"
INITIAL_SCRIPT="$2"
[ -z "$INITIAL_SOURCE" ] && INITIAL_SOURCE=/dev/null
[ -z "$INITIAL_SCRIPT" ] && INITIAL_SCRIPT=/dev/null
if [ "$#" -gt 2 ]; then
echo "Too many arguments" >&2
exit 1
fi
# Create a temporary file with content from stdin
# Usage: create_file HANDLE <CONTENT
# HANDLE is the name of the bash variable that will point to the file's path
create_file() {
local file
file="$temp_dir/$1"
if [ ! -t 0 ]; then
cat > "$file"
else
touch "$file"
fi
declare -g "$1"="$file"
}
# Same as create_file but also marks it executable by the current user
create_file_x() {
create_file "$@"
chmod u+x "${!1}"
}
# Create a fifo so the first and second windows can notify the third window of
# changes
fifo="$temp_dir/fifo"
mkfifo "$fifo"
# The source file (first buffer) that is being piped to SCRIPT
create_file SOURCE < "$INITIAL_SOURCE"
# The script file (second buffer) that will process the file and generate
# output in the third buffer
create_file_x SCRIPT < "$INITIAL_SCRIPT"
#
# Notifies the output terminal that some of the first two buffers have changed.
#
create_file_x on_change <<EOF
<<pipetest_on_change>>
EOF
#
# Supplementary vimrc file that is loaded by each vim session started from this
# program.
#
create_file vimrc <<EOF
<<pipetest_vimrc>>
EOF
#
# Vim wrapper that loads our supplementary vimrc file.
#
create_file_x custom_vim <<EOF
<<pipetest_custom_vim>>
EOF
#
# Output script - script that is run in the third buffer that shows the output
# of the user SCRIPT when applied to the SOURCE file.
#
create_file_x output_script <<EOF
<<pipetest_output_script>>
EOF
#
# Main script that launches tmux and everything.
#
create_file_x main_script <<EOF
<<pipetest_main_script>>
EOF
tmux new "$main_script"
#
# Print the resulting script
#
cat "$SCRIPT"
#
# Remove created temporary directory
#
rm -rf "$temp_dir"
These scripts are embedded into the pipetest
script as heredocs, but we define
them as code blocks here for better readability.
echo > "$fifo"
" On write run the on_change script
autocmd BufWritePost * silent !$on_change
autocmd ExitPre $SOURCE,$SCRIPT silent !tmux kill-session
"$vim_executable" -c "source $vimrc" "\$@"
echo "This is the output."
echo "It will automatically refresh when either of the files change."
echo "Press Ctrl+C here or quit any of the two vim instances to exit."
trap "tmux kill-session" INT TERM EXIT
while :; do
#stty -echo
read _ < "$fifo"
[ "$?" != "0" ] && break
clear
"$SCRIPT" <"$SOURCE"
done
tmux split-window -h "$custom_vim" "$SCRIPT"
tmux split-window -h sh -c 'cd "$(pwd)"; "$output_script"'
tmux select-pane -L
tmux select-layout even-horizontal
"$custom_vim" "$SOURCE"
browser='firefox -P «eval-user-name()»'
# If a firefox window is currently active, open the link in the active window
if xprop -id "$(xdotool getactivewindow)" | grep -qi 'firefox'; then
where='--new-tab'
else # Otherwise open a new window
where='--new-window'
fi
args="$(printf '%q ' "$@")"
i3-msg exec "$browser $where $args"
xdotool xorg-xprop
cmd="$(which -a "$1" | uniq | sed -n 2p)"
exec "$cmd" "${@:2}"
# Get https://termbin.com/<TERMBIN_BLOB> using curl
# Usage: curltb TERMBIN_BLOB [CURL_OPTIONS]
curl https://termbin.com/"$1" "${@:2}"
# Print out all 256 colors in the terminals
f=0
l=256
if [ -n "$1" ]; then
l="$1"
fi
if [ -n "$2" ]; then
f="$1"
l="$2"
fi
(x=`tput op` y=`printf %40s`;for i in $(seq "$f" "$l");do o=00$i;echo -e ${o:${#o}-3:3} \
`tput setaf $i;tput setab $i`${y// /=}$x;done)
Wrapper to myemacs that will load its first argument instead of opening it. The remaining args simply passed to myemacs as usual. The main use for this is as a shebang for elisp scripts. NOTE: It must be implemented in such a convoluted way as a shell script, because process management in emacs is shit.
tmpfile="$(mktemp)"
cat >"$tmpfile" <<'EOF_c478eee3'
<<stringify-arg-to-eval-option>>
EOF_c478eee3
if [ -t 0 ]; then
input_file="$(realpath "$1")"
else
input_file="$(mktemp)"
trap "$(printf "%q " rm -rf "$input_file")" ERR EXIT
cat >"$input_file"
fi
arg="$(
emacs --quick --batch --load "$tmpfile" "$input_file" "${@:2}"
)"
myemacs --no-tty --no-wait --eval "$arg" | sed '/^<<do-not-print-token>>$/d'
rm -f "$tmpfile"
(let* ((file (car argv))
(code `(progn
(defvar argv)
(defvar process-environment)
(defvar default-directory)
(let ((argv (list ,@(cdr argv)))
(process-environment (list ,@process-environment)))
(load ,file)
<<do-not-print-token>>))))
(prin1 code))
Emacsclient prints the result of evaluation of --eval
. The only non-hacky way I
know to disable this is to give it the --suppress-output
option. But this would also
suppress desirable output like calls to (message)
. So I put this token as the
last statement, and remove it from the output by piping to sed
.
"1617875f9593bd741b9637ad26aabb9f42ff3df769564f7881d7603a80ea0ae2"
Wrapper command around the equivalent fish function.
fish -C "o $(printf "%q " "$@")"
Get the OS type.
cat /etc/lsb-release 2>/dev/null | grep -q 'Ubuntu' && echo 'ubuntu' && exit
cat /etc/os-release 2>/dev/null | grep -q 'Arch Linux' && echo 'arch' && exit
echo 'Unsupported OS' >&2
exit 1
python -c 'import urllib.parse as p; print(p.quote(input()))'
python -c 'import urllib.parse as p; print(p.unquote(input()))'
mkfifo /tmp/fifo 2>/dev/null
session_exists() {
tmux has-session -t fifo 2>/dev/null
}
create_session() {
tmux new -s fifo "$@" tail --follow=name /tmp/fifo
}
if [ -t 0 ]; then
if session_exists; then
tmux attach -t fifo
else
create_session
fi
else
if ! session_exists; then
create_session -d
fi
tee /tmp/fifo
fi
# This piece of magic was generated by ChatGPT. Don't try to understand it, just
# accept it.
comm -23 <(seq 49152 65535 | sort) <(ss -tan | awk '{print $4}' | cut -d':' -f2 | sort -u) | shuf | head -n 1
tag="$(date +%s%3N | sha256sum | head -c 32)"
docker image build --tag "$tag" --no-cache -f - "$@" <<'EOF'
«docker-ls-context/dockerfile»
EOF
docker run --rm "$tag"
docker rmi "$tag"
FROM busybox
WORKDIR /workdir
COPY . .
CMD find .
echo -ne "\033[1;32m" >&2
echo "$@" >&2
echo -ne "\033[0m" >&2
echo -ne "\033[1;33m" >&2
bash -c 'printf "%q " "$@"' -s "$@" >&2
echo >&2
echo -ne "\033[0m" >&2
"$@"
set -- --on-top --thumb-size 32 "$@"
if which dragon-drag-and-drop &>/dev/null; then
dragon-drag-and-drop "$@"
elif which dragon-drop &>/dev/null; then
dragon-drop "$@"
else
alternative-command dragon "$@"
fi
content="$(cat)"
notify-send "tee-notify" "$content"
echo "$content"
{
timeout 0.5 openssl s_client -connect "$1" -showcerts 2>/dev/null
true
} | awk '/^-----BEGIN CERTIFICATE-----$/,/^-----END CERTIFICATE-----$/'
while :; do
command "$@"
sleep 2
done
Create a BitBucket PR.
uriEncode() {
tr -d '\n' | jq -sRr @uri
}
source_branch="$(git branch --show-current | tr -d '\n')"
source_branch_enc="$(uriEncode <<<"$source_branch")"
dest_branch="$(
{
git config init.defaultBranch
git branch | sed 's/^..//'
} | head -1
)"
dest_branch_enc="$(uriEncode <<<"$dest_branch")"
remote="$(git remote get-url upstream 2>/dev/null)"
if [ -z "$remote" ]; then
remote="$(git remote get-url origin)"
fi
if [ -z "$remote" ]; then
echo "Either upstream or origin remote must exist"
fi
if [[ ! "$remote" =~ .*bitbucket\.org.* ]]; then
echo "Only BitBucket is supported currently"
exit 1
fi
bitbucket_repo="$(
echo "$remote" |
sed 's_.*bitbucket\.org[:/]__' |
grep '^[^/]\+/[^/]\+' |
sed 's_\.git$__'
)"
url="https://bitbucket.org/$bitbucket_repo/pull-requests/new?source=$source_branch_enc&dest=$dest_branch_enc"
echo "Opening a PR in the browser..."
echo "Source branch: $source_branch"
echo "Destination branch: $dest_branch"
echo "BitBucket repo: $bitbucket_repo"
echo "PR URL: $url"
gtk-launch "$(xdg-settings get default-web-browser)" "$url"
mosquitto_sub \
-F '{"topic": "%t",\n"body": %p}' \
"$@" |
while :; do
echo
yq -P \
2> >(grep -v "Error: bad file '-': yaml: line [0-9]\+: did not find expected <document start>")
done
complete -c mosquitto_sub_yq --wraps mosquitto_sub
mosquitto_sub \
-F '{"topic": "%t",\n"body": %p}' \
"$@" |
while :; do
echo
jq
done
complete -c mosquitto_sub_jq --wraps mosquitto_sub
These are programs that are meant to be used mostly interactively. As such, they are designed to be easily integrated into dmenu scripts.
# Look up a configuration file by its user-friendly alias.
# Note: This script is statically parsed by lscf. Keep its structure intact.
for arg in "$@"; do
case "$arg" in
.haris) echo ~/.haris ;;
README.org) echo ~/.haris/README.org ;;
private.org) echo ~/.haris/private/README.org ;;
temporary.org) echo ~/.haris/temporary.org ;;
scripts.org) echo ~/.haris/scripts.org ;;
shells.org) echo ~/.haris/shells.org ;;
wm.org) echo ~/.haris/wm.org ;;
vcs.org) echo ~/.haris/vcs.org ;;
vim.org) echo ~/.haris/vim.org ;;
browser.org) echo ~/.haris/browser.org ;;
gui.org) echo ~/.haris/gui.org ;;
misc.org) echo ~/.haris/misc.org ;;
terminal.org) echo ~/.haris/terminal.org ;;
repl.org) echo ~/.haris/repl.org ;;
bootstrap.org) echo ~/.haris/bootstrap/README.org ;;
background.org) echo ~/.haris/background.org ;;
alacritty) echo ~/.haris/terminal.org ;;
alias-tmp) echo ~/.alias-tmp ;;
alias-gui-tmp) echo ~/.alias-gui-tmp ;;
fish) echo ~/.haris/shells.org ;;
fish-private) echo ~/.config/fish/private.fish ;;
fish-tmp) echo ~/.config/fish/tmp.fish ;;
vifm) echo ~/.haris/terminal.org ;;
git) echo ~/.haris/vcs.org ;;
gh) echo ~/.haris/vcs.org ;;
hg) echo ~/.haris/vcs.org ;;
picom) echo ~/.haris/wm.org ;;
dunst) echo ~/.haris/misc.org ;;
tem) echo ~/.haris/terminal.org ;;
mime) echo ~/.haris/README.org ;;
zathura) echo ~/.haris/gui.org ;;
emacs) echo ~/.haris/emacs.org ;;
spacemacs) echo ~/.spacemacs ;;
cron) echo ~/.crontab ;;
octave) echo ~/.octaverc ;;
python) echo ~/.startup.py ;;
tuterm) echo ~/.haris/terminal.org ;;
xinit) echo ~/.haris/wm.org ;;
sxhkd) echo ~/.haris/wm.org ;;
mpv) echo ~/.haris/gui.org ;;
flameshot) echo ~/.haris/gui.org ;;
cheat) echo ~/.haris/terminal.org ;;
monero) echo ~/.config/monero-project/monero-core.conf ;;
xmrig) echo ~/.config/xmrig.json ;;
tmux) echo ~/.tmux.conf ;;
esac
done
# Run an elisp interpreter through emacs
<<add-create-frame-option-if-term-dumb>>
myemacs --eval "(ielm)" "$@"
# Run octave interpreter through emacs
<<add-create-frame-option-if-term-dumb>>
myemacs --eval "(progn (run-octave) (delete-other-windows))" "$@"
# Run a python interpreter through emacs
<<add-create-frame-option-if-term-dumb>>
myemacs --eval "(progn (call-interactively 'run-python) (delete-other-windows))" "$@"
# Run an emacs-hosted terminal via vterm
<<add-create-frame-option-if-term-dumb>>
myemacs --socket-name=vterm --eval '(multi-vterm)' "$@"
(with-selected-frame (make-frame `((name . "EmacsMan")
(display . ,(getenv "DISPLAY"))
(environment . ,process-environment)))
(let ((man-buffer nil))
(defvar haris/last-man-buffer nil "The last man buffer that was opened")
(setq man-buffer
(if argv
(man (car argv))
(progn
(when (and (boundp 'haris/last-man-buffer)
(buffer-live-p haris/last-man-buffer))
(switch-to-buffer haris/last-man-buffer))
(call-interactively 'man))))
(when man-buffer (setq haris/last-man-buffer man-buffer))))
complete -c eman --wraps man
(with-selected-frame (make-frame `((name . "EmacsFloat")
(display . ,(getenv "DISPLAY"))
(environment . ,process-environment)))
(spacemacs/switch-to-scratch-buffer)
(with-temp-buffer
(insert (shell-command-to-string "~/.local/lib/haris/etranslate-helper 2>/dev/null"))
(let* ((gt-taker-text 'buffer)
(gt-buffer-prompt-window-config '(display-buffer-same-window))
(gt-buffer-render-window-config gt-buffer-prompt-window-config))
(gt-do-translate))))
This script prints the contents of the clipboard
# In order to get the time when the selection happened I use a timestamp
# reported by xclip and to get the current time I use a timestamp reported by
# xsel. I have no idea what these timestamps are relative to, but I have
# empirically determined that this works.
set -e
ts_current="$(
timeout 0.5 \
xsel --output -vvv 2>&1 >/dev/null \
| grep '^xsel: Timestamp:' \
| awk '{print $3}'
)"
ts_sel="$(timeout 0.5 xclip -target TIMESTAMP -out 2>/dev/null)"
# Uncomment for debugging:
# echo "current timestamp: $ts_current, selection timestamp: $ts_sel" >&2
elapsed_time_millis="$(expr "$ts_current" - "$ts_sel" 2>/dev/null)"
status="$?"
# Some programs (alacritty) don't handle the primary clipboard correctly,
# so ts_sel might not contain a valid timestamp
if [ "$status" != 0 ]; then
exit "$status"
fi
if [ "$elapsed_time_millis" -lt "10000" ]; then
xsel --primary --output
fi
# Open emacs and run ERC in it
<<add-create-frame-option-if-term-dumb>>
myemacs --socket-name="irc" \
--eval "(unless erc-server-connected (call-interactively 'erc-tls))" \
"$@"
(let ((dir default-directory))
(with-selected-frame (make-frame `((name . "EmacsFloat")
(display . ,(getenv "DISPLAY"))
(environment . ,process-environment)))
(setq-local default-directory dir)
(spacemacs/switch-to-scratch-buffer)
(run-with-timer 0.3 nil 'docker)))
(let ((dir default-directory))
(with-selected-frame (make-frame `((name . "EmacsFloat")
(display . ,(getenv "DISPLAY"))
(environment . ,process-environment)))
(switch-to-buffer (get-buffer-create
(generate-new-buffer-name haris/custom-temp-buffer-name)))
(setq-local default-directory dir)
(setq-local process-environment (cdr (assq 'environment
(frame-parameters
(selected-frame)))))
(run-with-timer 0.3 nil 'docker-compose)))
myemacs-float -c --eval '(bluetooth-list-devices)'
<<add-create-frame-option-if-term-dumb>>
myemacs-float --eval '(progn (proced) (delete-other-windows))' "$@"
(let ((dir default-directory))
(with-selected-frame (make-frame `((name . "EmacsFloat")
(display . ,(getenv "DISPLAY"))
(environment . ,process-environment)))
(let ((default-directory dir))
(magit-status))))
# Copy, show or open the argument based on its content
copy_or_show_or_open() {
notify-send 'QR Code:' "$@"
echo "$1" | xsel -b
if echo "$1" | grep -q '^https://'; then
firefox --new-tab "$@"
fi
}
if [ "$1" = 'in' ]; then
copy_or_show_or_open "$(timeout 20s zbarcam /dev/video0 -1 | sed 's/^QR-Code://')"
elif [ "$1" = 'screen' -o "$1" = 's' ]; then
copy_or_show_or_open "$(zbarimg -q <(flameshot screen --raw) | sed 's/^QR-Code://')"
else # out
if [ -t 0 ] || [ "$TERM" = 'linux' ]; then
input="$(xsel -b -o)"
else
input="$(cat)"
fi
echo "$input" | qrencode -s 10 -o - | feh -
fi
zbar qrencode
# One-time reboot into selected OS
set -e # Quit if any command fails
index="$(grep "menuentry '\|submenu '" /boot/grub/grub.cfg |\
grep -v -P '\t' |\
grep -i -n "$1" |\
head -1 | awk -F':' '{print $1}')"
if [ -z $index ]; then
echo "No entry found"
else
index=$(( $index - 1 ))
echo "Selected menuentry: $index. Proceed?"
read response
if [ "$response" == 'y' ]; then
sudo grub-reboot $index >/home/haris/src/grublog 2>&1
reboot
fi
fi
# Like regular vimdiff, but in nvim
nvim -d "$@"
# Find and open in vim a header file from the default include path
vim "$(echo "#include <$1>" | cpp -H 2>&1 >/dev/null | head -1 | sed 's/^. //')"
# Open alacritty with pydoc in it
# - All arguments are passed to pydoc
# - Alacritty window class tracks those defined in my i3 config
alacritty --class Alacritty,Float -e fish -C "pydoc $*" &
# Run rg and fzf to search through file contents and jump to a file
where="$1"
[ -z "$where" ] && where='.'
rg --column --line-number --no-heading --color=always --smart-case . | fzf --ansi
import shlex
import subprocess
import dryparse
from dryparse.objects import Option
import sys
from urllib.parse import urlparse, parse_qs
@dryparse.command
def otp(name: str, *, new: bool = False, extract: Option("-x", "--extract", bool) = False):
"""Use and manage one-time passwords.
:param name: name of the OTP
:param new: store a new OTP instead of printing an existing one
:param extract: Extract the secret from the URL given as argument
"""
extract: bool
def run(*args, **kwargs):
return subprocess.run(*args, shell=True, encoding="utf-8", **kwargs)
if new and extract:
print("--new and --extract can't be used together", file=sys.stderr)
sys.exit(1)
if new:
p = run(f"pass insert {name}/otp-secret")
sys.exit(p.returncode)
elif extract:
query = parse_qs(urlparse(name).query)
print(query["secret"][-1])
return
otp_secret = run(
f"pass show {name}/otp-secret", stdout=subprocess.PIPE
).stdout.strip()
p = run(f"oathtool --totp --base32 {shlex.quote(otp_secret)}")
sys.exit(p.returncode)
otp = dryparse.parse(otp, sys.argv)
otp()
dryparse
# List of all blocks
listing="$(lsblk --list -o PATH,LABEL)"
# ... with header removed
items="$(
echo "$listing" \
| tail -n +2 \
| sed 's/.*/"&"/' \
| nl --number-width=1 \
| grep -v '/dev/loop'
)"
# number of lines
lineno="$(echo "$items" | wc -l)"
# Open file descriptor 3
exec 3>&1
# Show dialog and store id of selection
id=$(
eval dialog 2>&1 1>&3 \
--default-item $lineno \
--menu '"Choose a device/partition:"' 60 50 $lineno $items
)
[ "$?" != 0 ] && exit 1 # Dialog exited with error
# Select mounting directory
mount_dir="$(dialog --fselect ~/mnt/ 60 50 2>&1 1>&3)"
[ "$?" != 0 ] && exit 1 # Dialog exited with error
# Clear but keep scrollback buffer
clear -x
if [ ! -d "$mount_dir" ]; then # Nonexisting mount directory
read -n 1 -p\
"The directory $mount_dir does not exist and will be created. Continue? [y/n]: " \
choice 1>/dev/null
[ "$choice" != "y" ] && exit 1
echo # newline
mkdir "$mount_dir"
fi
# Get path to selected device
device="$(echo "$items" | sed -n ${id}p | sed 's_.*"\(\S*\)\s.*_\1_')"
read -n 1 -p\
"$device will be mounted at $mount_dir. Continue? (requires sudo) [y/n]: " \
choice
echo # newline
[ "$choice" != "y" ] && exit 1
# Mount the device at last
sudo mount "$device" "$mount_dir" -o umask=002,uid=$(id -u),gid=$(id -g)
echo -e "$mount_dir" > /tmp/imount_directory
chmod a+rw /tmp/imount_directory
# Temporarily bind keys
pkill -f '^sxhkd\.tmp '
myemacs-float --wait -c ~/.tmp.sxhkdrc
notify-send "kbind" "Applying config"
o --silent sxhkd.tmp
Create a Xephyr session for the current user that tries to mimic a regular
startx
session as closely as possible.
# Option variables
host_display="$DISPLAY"
display=1"$(id -u "$USER")"
Xephyr :"$display" -once -resizeable \
-screen "$(xdpyinfo | grep dimensions | grep -P '\d+x\d+' --only-matching | head -1)" \
"$@" &
sleep 0.5s
windowid="$(xdotool getactivewindow)"
echo "WINDOW ID: $windowid"
export DISPLAY=:"$display"
i3 &
sxhkd &
dunst &
DISPLAY="$host_display" xev -id "$windowid" -event structure |
while read line; do
if grep "^ConfigureNotify event" <<<"$line"; then
xrandr
fi
done
# Custom configuration
HARIS_WALLPAPER_CHANGE=true
HARIS_WALLPAPER_CHANGE_INTERVAL_SEC=60
# Configure xsecurelock
export XSECURELOCK_LIST_VIDEOS_COMMAND="ls-wallpapers | grep -P '\.(mp4|webm|avi|mkv|gif)$'"
export XSECURELOCK_AUTH_TIMEOUT=10
export XSECURELOCK_BLANK_TIMEOUT=1200
export XSECURELOCK_BLANK_DPMS_STATE=suspend
export XSECURELOCK_SAVER=saver_mpv
export XSECURELOCK_IMAGE_DURATION_SECONDS=3
export XSECURELOCK_VIDEOS_FLAGS='--loop --panscan=1 --autofit=100%x100%'
export XSECURELOCK_SAVER_RESET_ON_AUTH_CLOSE=0
export XSECURELOCK_PASSWORD_PROMPT=asterisks
export XSECURELOCK_KEY_Super_L_COMMAND='systemctl suspend'
if [ -z "$($XSECURELOCK_LIST_VIDEOS_COMMAND)" ]; then
# No videos found, use images
export XSECURELOCK_LIST_VIDEOS_COMMAND="ls-wallpapers | grep -P '\.(png|jpg|jpeg|webp)$'"
export XSECURELOCK_VIDEOS_FLAGS=''
HARIS_WALLPAPER_CHANGE=''
fi
picom_running="$(pgrep '^picom$' >/dev/null && echo true)"
pkill '^picom$'
# Kill gpg-agent
pkill gpg-agent 2>/dev/null
# Run xsecurelock, restarting at intervals in order to change the wallpaper
xsecurelock &
xsecurelock_pid="$!"
last_change_time="$(date +%s)"
# If a suspend was the trigger for this lock, make sure the suspend is delayed
# until xsecurelock is ready. This makes sure the screensaver is active
# immediately after a wake-up.
if [ -n "$XSS_SLEEP_LOCK_FD" ]; then
exec "$XSS_SLEEP_LOCK_FD"<&-
fi
# Restart xsecurelock at intervals to change the wallpaper,
# but without interrupting the user if they are entering their password.
while [ -n "$HARIS_WALLPAPER_CHANGE" ]; do
sleep 1s
if ! ps -p "$xsecurelock_pid" >/dev/null; then
# xsecurelock exited, exit this program as well
break
fi
# Check if the wallpaper has been active for enough time
now="$(date +%s)"
change_interval="$HARIS_WALLPAPER_CHANGE_INTERVAL_SEC"
diff="$(($now - $last_change_time))"
echo "Wallpaper has been active for $diff s."
if [ "$diff" -ge "$change_interval" ]; then
echo "Wallpaper change is due"
idle_time="$(xprintidle)"
if [ "$idle_time" -ge "${XSECURELOCK_AUTH_TIMEOUT}000" ]; then
echo "Changing wallpaper"
last_change_time="$(date +%s)"
xsecurelock &
new_pid="$!"
sleep 0.3s
kill "$xsecurelock_pid"
xsecurelock_pid="$new_pid"
else
echo "Wallpaper change skipped due to user activity. idle_time: $idle_time ms"
fi
fi
done
if [ -n "$picom_running" ]; then
sleep 0.5s
o picom
fi
export HARIS_CALLER_IS_DMENU=true
CACHE_FILE=~/.cache/haris/desktop-apps.txt
mkdir -p "$(dirname "$CACHE_FILE")"
# If an argument is provided, run corresponding custom dmenu script
if [ -n "$1" ]; then
script=~/.local/lib/dmenu/"$1"
if [ -e "$script" ]; then
"$script"
exit
fi
fi
# Otherwise open a generic dmenu where the user will choose what dmenu script
# or other program to run
run_script() {
if [ -n "$TMUX" ]; then
~/.local/lib/dmenu/"$1"
else
o --silent ~/.local/lib/dmenu/"$1"
fi
}
pull_desktop_apps() {
# Print out desktop apps by reading *.desktop files and also cache them
CACHE_FILE="$CACHE_FILE" sh <<'EOF'
«pull_desktop_apps»
EOF
}
get_desktop_apps() {
age="$(date -d "now - $(stat -c '%Y' "$CACHE_FILE") seconds" +%s)"
# Refresh the cache only if the file is older than a specified age (seconds)
{
if [ ! -f "$CACHE_FILE" -o $age -gt 36000 ]; then
pull_desktop_apps
else
cat "$CACHE_FILE"
fi
} | sort | uniq
}
get_custom_scripts() {
find ~/.local/bin -executable -type f -printf '%f\n' | sort | uniq | sed 's/^/ /'
}
# ┏━━━━━━━━━━━━━━━┓
# ┃ dmenu entries ┃
# ┗━━━━━━━━━━━━━━━┛
get_dmenu_entries() {
echo " Open" # Open an URL or bookmark
echo " Clipboard" # Clipboard using clipmenu
echo " Snippets" # Text snippets
echo " TODO" # Open TODO file of a project
echo " Windows" # Choose windows
echo " Pacman" # Package management
echo " Color" # Pick a color
echo " Unicode" # Pick an icon
echo " Kill Process" # Kill process
echo " Fix Wifi" # Fix Wi-Fi drop issue on some networks
echo " Config" # Open documentation selection
echo " System" # System actions
echo " Services" # Control systemd services
echo " Update cache" # Update desktop app cache
echo "Tem" # Launch tem development environment
echo "Octave" # Launch octave in emacs
echo " Python" # Launch python interpreter in emacs
echo " GPG" # GPG addresses
echo " OTP" # Generate OTP for selected service
echo " Books" # Pick a book to read
echo " IRC" # Open emacs client for IRC
echo "Quickmenu" # Menu to quickly revisit recent activity
get_desktop_apps # Programs extracted from *.desktop files
get_custom_scripts # Programs from ~/.local/bin
}
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
# ┃ Actions based on user's choice ┃
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
if [ -n "$1" ]; then
choice="$1"
else
choice="$(get_dmenu_entries | dmenu)"
[ "$?" = "0" ] || exit
if grep -q '^' <<<"$choice"; then
is_dotdesktop_app=true
fi
# If it starts with a non-ascii character (as in get-commands), remove it and the space following it
if grep -qP "[^\x00-\x7F]" <<<"$choice"; then
choice="$(cut -f2- -d' ' <<<"$choice")"
fi
fi
case "$choice" in
"Open")
run_script open ;;
"Clipboard")
clipmenu ;;
"Snippets")
run_script snips ;;
"TODO")
run_script todo ;;
"Windows")
~/.local/lib/i3/i3-container-commander.py ;;
"Pacman")
run_script pacman ;;
"Color")
run_script color ;;
"Unicode")
run_script unicode ;;
"Kill Process")
run_script pkill ;;
"Fix Wifi")
fix-wifi ;;
"Config")
run_script config ;;
"System")
run_script system ;;
"Services")
run_script services ;;
"Update cache")
rm "$CACHE_FILE" ;;
"Tem")
alacritty -e fish -C 'pj tem; clear' ;;
"Octave")
eoctave -c ;;
"Python")
epython -c ;;
"GPG")
run_script gpg ;;
"OTP")
run_script otp ;;
"Books")
run_script books ;;
"IRC")
myemacs --socket-name="irc" -c ;;
"Quickmenu")
run_script quickmenu ;;
# The rest: aliases and regular commands
*)
if [ -n "$is_dotdesktop_app" ]; then
# If the choice is an x.desktop app, launch it
o gtk-launch "$choice"
exit
fi
# Fallback, if the entry matches none of the above, just run the command
fish -c "o $choice"
;;
esac
This code block separated so that it can be run in sh
instead of bash
and thus
possibly benefit from a performance boost, in case sh
happens to point to a more
lightweight shell.
ls -1 $(echo "$XDG_DATA_DIRS" | tr ':' '\n')/applications/*.desktop |
sort | uniq |
sed -E 's|.*/([^/]+)\.desktop$| \1|' | tee "$CACHE_FILE"
Open a website in Firefox. Suggests bookmarks managed by buku, but you can input any URL. To suggest additional URLs, you can create the script ~/.local/lib/haris/custom/dmenu/open.sh, which should print a list of additional URLs (each on a new line) to display in this menu.
edit=" Edit..."
sync=" Sync..."
firefox_about_pages() {
cat <<'EOF_e245552b'
about:about
about:addons
about:buildconfig
about:cache
about:certificate
about:config
about:crashes
about:credits
about:devtools
about:downloads
about:home
about:license
about:memory
about:networking
about:newtab
about:performance
about:plugins
about:preferences
about:privatebrowsing
about:profiles
about:protections
about:rights
about:robots
about:serviceworkers
about:sessionrestore
about:support
about:telemetry
about:welcome
about:studies
EOF_e245552b
}
choice="$(
{
echo "$edit"
echo "$sync"
unbuffer buku -p --format 30 | grep -v '^$' | sort
[ -x ~/.local/lib/haris/custom/dmenu/open.sh ] && ~/.local/lib/haris/custom/dmenu/open.sh
firefox_about_pages
} | dmenu -p 'Open:'
)"
[ -z "$choice" ] && exit
if [ "$choice" = "$edit" ]; then
# Open this file for editing
gvim "$0"
elif [ "$choice" = "$sync" ]; then
alacritty --class Alacritty,Float -e fish -C "
echo -e \"--- Importing bookmarks from Firefox ---\nDefault is: n y y \";
buku --import ~/.mozilla/firefox/haris/bookmarks.html"
else
# Try to open it as a bookmark in firefox
url="$(
buku --sreg "^$choice\$" -n 1 --format 10 |
grep -v 'waiting for input'
)"
[ -z "$url" ] && url="$choice"
# All google links shall be opened in firefox
echo "$url" | grep -q 'google' && browser="$(echo "$browser" | sed 's_librewolf_/bin/firefox/')"
echo "$url"
auto-browser -P haris "$url"
fi
edit=" Edit..."
lookup="$(
ls ~/proj/*/TODO.org \
~/proj/drytoe/*/TODO.org \
~/data/personal/todos/*/TODO.org
)"
echo "$lookup"
entries="$(echo "$edit"
echo "$lookup" |
while read p; do
basename "$(dirname "$p")"
done)"
choice="$(
echo "$entries" | dmenu -p TODO:
)"
[ "$?" != 0 ] && exit 1
if [ "$choice" = " Edit..." ]; then
cd "$(dirname "$0")"; gvim "$0"
else
myemacs-float "$(echo "$lookup" | grep "/$choice/TODO.org")"
fi
install=" Install..."
about=" About..."
remove=" Remove..."
manage=" Manage..."
update=" Update..."
keyring=" Keyring..."
edit=" Edit..."
print_options() {
echo "$install"
echo "$about"
echo "$update"
echo "$remove"
echo "$manage"
echo "$keyring"
echo "$edit"
}
get_fish_cmd() {
echo "echo 'Running command: $*'"
echo "$*"
}
choice="$(print_options | dmenu -l $(print_options | wc -l))"
case "$choice" in
"$install")
cache_file=~/.cache/.aur-package-list.txt
age="$(date -d "now - $(stat -c '%Y' "$cache_file") seconds" +%s)"
# Create cache file if it does not exist or is older than 5 hours
if [ ! -f "$cache_file" ] || [ $age -gt 18000 ]; then
curl -s 'https://aur.archlinux.org/packages.gz' \
-o - | gunzip -c > "$cache_file"
fi
# Pull the list of AUR packages
list="$(cat "$cache_file")"
# Prepend official packages to the list
list="$(pacman -Ssq; echo "$list")"
choice="$(echo "$list" | dmenu -l 20)"
[ -z "$choice" ] && exit
cmd="$(pacman -Ss "^$choice\$" >/dev/null && echo sudo pacman -S || echo paru)"
alacritty --class Alacritty,Float -e fish -C "$(get_fish_cmd $cmd $choice)" && exit
;;
"$about")
choice="$(echo "$(pacman -Qq)" | dmenu -p 'About:' -l 20)"
[ -z "$choice" ] && exit
alacritty --class Alacritty,Float -e fish -C "$(get_fish_cmd pacman -Qi $choice)" && exit
;;
"$update")
alacritty --class Alacritty,Float -e fish -C "paru -Syu"
;;
"$remove")
choice="$(pacman -Qq | dmenu -l 20)"
[ -z "$choice" ] && exit
alacritty --class Alacritty,Float -e fish -C "$(get_fish_cmd sudo pacman -R $choice)" && exit
;;
"$manage")
myemacs-float "$(fcmd system-install)"
;;
"$keyring")
alacritty --class Alacritty,Float -e fish -C "$(get_fish_cmd sudo pacman -Sy archlinux-keyring && exit)"
;;
"$edit")
gvim "$0"
;;
esac
declare -A colors
colors[',k black']=' #1e1e1e'
colors[',r red']=' #ff5555'
colors[',g green']=' #5ac2a8'
colors[',y yellow']=' #f2b374'
colors[',b blue']=' #6980fa'
colors[',m magenta']=' #d098ff'
colors[',c cyan']=' #8cceff' # TODO Change to something darker
colors[',w white']=' #92aab7'
colors['.k brblack']=' #6b746b'
colors['.r brred']=' #ff8c8c'
colors['.g brgreen']=' #98eb98'
colors['.y bryellow']=' #e0d97b'
colors['.b brblue']=' #99a3ff'
colors['.m brmagenta']=' #f298c3'
colors['.c brcyan']=' #a6d9ff'
colors['.w brwhite']=' #dddddd'
get_entries() {
echo ' Edit...'
printf '%s\n' "${!colors[@]}" | sort | sed 's_.*_ &_'
}
entries=$(get_entries)
let n=$(echo "$entries" | wc -l)
choice="$(echo "$entries" | dmenu -l $n -p 'Color:')"
[ -z "$choice" ] && exit
if [ "$choice" = ' Edit...' ]; then
gvim "$0"
exit
fi
# Remove decoration from the choice
choice_filtered="$(echo $choice | sed 's_[^ ]* *\(.*\)_\1_')"
# Copy the color, after removing whitespace
echo -n "${colors["$choice_filtered"]}" | sed 's_[^ ]* *\(.*\)_\1_' | xsel -b
# Choose a font-awesome icon from dmenu and copy it
from urllib.request import urlopen
from subprocess import run, PIPE
import os.path
import yaml
# ┏━━━━━━━━━━━━━━━━━━━━━━━┓
# ┃ Prepare the icon list ┃
# ┗━━━━━━━━━━━━━━━━━━━━━━━┛
cache_file = os.path.expanduser('~/.cache/font-awesome-icon-list.yml')
# Read the yml file from cache, or download it from GitHub
if os.path.exists(cache_file):
text = open(cache_file).read()
else:
url = 'https://raw.githubusercontent.com/FortAwesome/Font-Awesome/6.x/metadata/icons.yml'
data = urlopen(url).read()
text = data.decode('utf-8')
open(cache_file, 'w').write(text)
# Read the YAML file
data = yaml.load(text, yaml.Loader)
# ┏━━━━━━━━━━━━━━━━━━━━┓
# ┃ Add custom options ┃
# ┗━━━━━━━━━━━━━━━━━━━━┛
top_entries = [
' Edit...',
' FontAwesome...',
' From code...',
' Get code...',
]
char_entries = []
# Create a (decorated) list of entries
for key in data.keys():
unicode = int(data[key]['unicode'], base=16)
char_entries.append(chr(unicode) + ' ' + key)
def add_custom(char):
global char_entries
char_entries.append(char + ' [custom]')
# ┏━━━━━━━━━━━━━━━━━━━━━━━┓
# ┃ Add custom characters ┃
# ┗━━━━━━━━━━━━━━━━━━━━━━━┛
add_custom('├ |-')
add_custom('└ |_')
add_custom('─ --')
add_custom('┃┗━┛┏━┓ ||')
add_custom('š .sh')
add_custom('ć .ch meko')
add_custom('č .ch tvrdo')
add_custom('đ .dj')
add_custom('ž .zj')
# Form entry lists as multi-line strings
char_entries = '\n'.join(char_entries)
# Add options and character entries together
top_entries = '\n'.join(top_entries) + '\n' + char_entries
# Run dmenu and get user choice
p = run(['dmenu'], stdout=PIPE, input=top_entries, encoding='utf-8')
choice = p.stdout[:-1]
def copy_to_clipboard(text):
run(['xsel', '-b'], input=text, encoding='utf-8')
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
# ┃ Actions based on user's choice ┃
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
if choice[2:] == 'Edit...': # Open this file for editing
run(['alacritty', '--class', 'Alacritty,Float', '-e', 'fish', '-C',
'cd (dirname ' + __file__ + '); vim -c "norm 45z." ' + __file__])
elif choice[2:] == 'FontAwesome...':
run(['firefox', 'https://fontawesome.com/search'])
elif choice[2:] == 'From code...':
p = run(['dmenu', '-p', 'Code:'], stdout=PIPE, input=char_entries, encoding='utf-8')
open('/home/haris/src/testlog', 'w').write(choice)
code = p.stdout[:-1]
if choice:
copy_to_clipboard(chr(int(code, base=16)))
elif choice[2:] == 'Get code...':
p = run(['dmenu', '-p', 'Character:'], stdout=PIPE, input=char_entries, encoding='utf-8')
choice = p.stdout[:-1]
if choice:
copy_to_clipboard(str(ord(choice[0])))
elif choice:
copy_to_clipboard(choice.split(' ')[0])
choice="$(ps -A -o comm --no-headers | dmenu)"
[ -z "$choice" ] && exit
process="$choice"
choice="$(echo " No\n Yes, kill $process" | dmenu -p 'Sure?' -l 2)"
[ "$choice" = " Yes, kill $process" ] && pkill "$process"
from subprocess import run, PIPE
import os
import os.path
import sys
# Load regular configuration entries
entries = run('lscf', stdout=PIPE, encoding='utf-8').stdout.replace('-', ' ')
# Load dmenu scripts
dmenu_scripts = os.listdir(os.path.expanduser('~/.local/lib/dmenu/'))
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
# ┃ Additional entries and customization ┃
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
entries = (
' Edit...\n' +
' ' + entries.replace('\n', '\n ') +
'dmenu\n dmenu ' +
'\n dmenu '.join(dmenu_scripts)
)
# Run dmenu
choice = run(['dmenu', '-l', '20', '-p', 'Config:'],
input=entries, encoding='utf-8', stdout=PIPE).stdout
if not choice:
sys.exit()
# Strip decoration from the entry
choice = choice[2:-1].replace(' ', '-')
def run_command(cmd):
run(['alacritty', '--class', 'Alacritty,Float', '-e',
'fish', '-C', cmd])
# ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
# ┃ Actions based on user's choice ┃
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
if choice == 'dmenu':
EVAL = fr'''
(progn (find-file "~/.haris/scripts.org")
(call-interactively (swiper "^\\* Dmenu"))
(evil-ex-nohiglight))
'''
run(["myemacs-float", "--eval", EVAL])
if choice.startswith('dmenu-'):
submenu = choice.replace("dmenu-", "")
submenu = submenu[0].upper() + submenu[1:]
run(["notify-send", submenu])
EVAL = fr'''
(progn (find-file "~/.haris/scripts.org")
(call-interactively (swiper "^\\*\\* {submenu}"))
(evil-ex-nohiglight))
'''
run(["myemacs-float", "--eval", EVAL])
elif choice == 'Edit...':
EVAL = '''
(progn (find-file "~/.haris/scripts.org")
(goto-char (org-find-property "CUSTOM_ID" "cf"))
(evil-ex-nohiglight))
'''
run(["myemacs-float", "--eval", EVAL])
else:
run("fish -c 'ecf {}'".format(choice), shell=True)
sys.exit()
choice="$(echo "Suspend\nSuspend & Lock\nShutdown\nReboot..." | dmenu)"
# No choice, bye-bye
[ -z "$choice" ] && exit
if [ "$choice" = "Shutdown" ]; then
choice="$(echo " No\n Yes, shutdown" | dmenu -p 'Sure?')"
[ "$choice" = " Yes, shutdown" ] && shutdown now
elif [ "$choice" = "Suspend & Lock" ]; then
xlock & # NOTE: using the 'o' script here doesn't detach tmux for some reason
sleep 1
systemctl suspend -i
elif [ "$choice" = "Suspend" ]; then
systemctl suspend -i
elif [ "$choice" = "Reboot..." ]; then
print_entries() {
# Extract only lines with menu entries from grub
grep "menuentry '\|submenu '" /boot/grub/grub.cfg |
# Only top-level menus are considered
grep -v -P '\t' |
# Take only the entry name
sed "s_\S* '\([^']*\)'.*_\1_" |
# Add numbers
nl -w 1 -v 0 -n rn | sed -E 's/\s+/ /g'
}
entries="$(print_entries)"
choice="$(echo "$entries" | dmenu -l $(echo "$entries" | wc -l) | egrep --only-matching '^[0-9]+')"
[ -z "$choice" ] && exit
# For convenience, grub-reboot should be allowed passwordless in sudoers.
sudo grub-reboot "$choice"
reboot || sudo reboot
fi
# Unicode glyphs
started=""
stopped=""
arrow=""
restart=""
user=""
list() {
systemctl "list-$1" \
--all \
--full \
--plain \
--no-pager \
--no-legend \
--type=service 2>&- \
"${@:2}" \
| cut -f1 -d' '
}
get_prefix() {
if [ "$1" == "--user" ]; then \
echo -n "$user "
else
echo -n " " # NOTE: This string contains a THIN SPACE unicode character
fi
}
entries() {
local opt="$1" running_services stopped_services
readarray -t running_services < <(list units --state=running $opt)
# Running services
paste -d' ' <(
systemctl show "${running_services[@]}" \
$opt \
| grep ^Restart= \
| sed -e "s/^Restart=no$/$started $arrow $stopped/" \
-e "s/^Restart=.*/$started $arrow $restart/" \
-e "s/^/$(get_prefix $opt)/"
) \
<(printf "%s\n" "${running_services[@]}")
# Stopped services
{
for state in exited failed; do
list units --state="$state" $opt
done
} | sed -e "s/^/$stopped $arrow $started /" \
-e "s/^/$(get_prefix $opt)/"
# Other services
if [ -z "$opt" ]; then
comm -23 \
<(list unit-files --state=disabled $opt | sort) \
<(printf "%s\n" "${running_services[@]}" | sort) \
| sed "s/^/$(get_prefix $opt)$stopped $arrow $started /"
fi
}
selection="$(
{
entries
entries --user
} | dmenu -p 'Services:' -l 20
)"
if [ -z "$selection" ]; then
exit 1
fi
service="$(echo "$selection" | grep --only-matching '\S\+$')"
user_opt=''
sudo='sudo'
if grep -q "$user" <<<"$selection"; then
echo here
user_opt="--user"
sudo=""
fi
cmd=''
if grep -q "$started $arrow $stopped" <<<"$selection"; then
cmd=stop
elif grep -q "$stopped $arrow $started" <<<"$selection"; then
cmd=start
else
cmd=restart
fi
set -e
# Notify the user
service_name="${service%.service}"
# Execute the action
export HARIS_BACKGROUND_TASKS_SILENT=true
o sh -c "SUDO_ASKPASS=\"$(which askpass-gui)\" $sudo systemctl '$cmd' $user_opt '$service'"
jctl_session_name="$(
tmux new -P -F'#{session_name}' -d journalctl --follow --lines=0 $user_opt -u "$service"
)"
action="$(
case "$cmd" in
start)
notify-send --action 0 "Starting service..." "$service_name"
;;
restart)
notify-send --action 0 "Restarting service..." "$service_name"
;;
stop)
notify-send --action 0 "Stopping service..." "$service_name"
;;
esac
)"
if [ "$action" = "0" ]; then
alacritty-float -e tmux attach -t "$jctl_session_name"
else
tmux kill-session -t "$jctl_session_name"
fi
# Get list of all public keys
keylist=($(gpg --list-public-keys Haris | grep '^\s' | sed 's/^\s*//g'))
entries="$(
for key in "${keylist[@]}"; do
# Get info for key
keyinfo="$(gpg --list-public-keys | grep "$key" -A1)"
# Get email of key owner
email="$(echo "$keyinfo" | grep '<.*>' | sed 's/^.*\]//')"
echo "$key" "$email"
done
)"
let n="$(echo "$entries" | wc -l)"
choice="$(echo "$entries" | dmenu -l $n -p 'GPG:')"
echo "$choice" | awk '{print $1}' | xsel -b
# Select an app and copy its OTP to clipboard
cd ~
entries="$(
fd 'otp-secret.gpg' .password-store -x echo {//} \
| sed 's:^\.password-store/\?::'
)"
choice="$(echo "$entries" | dmenu -l 10 -p 'OTP:')"
[ -z "$choice" ] && exit 1
otp "$choice" | xsel -b -t 30000
notify-send "OTP" "Saved to clipboard"
cd ~/data/literature
choice="$(fd --type file '' | dmenu -l 10 -p 'Book:')"
[ -z "$choice" ] && exit
sioyek "$choice"
sts="$?"
sleep 2s
trap 'kill "$pid"' EXIT
# trap 'printarg kill "$pid"' ERR
# Ensure that this script stays running as long as the program, so `o` doesn't
# time out
while :; do
pid="$(pgrep -a '' | grep --fixed-strings "$choice" | cut -f1 -d' ' | head -1)"
if [ -z "$pid" ]; then break; fi
sleep 5s
done
The quickmenu script is optional and must be provided separately at ~/.local/lib/dmenu/quickmenu.
# Root menu (dmenu_run)
clipmenu
# Open menu
xdotool xorg-xprop
# Open menu
buku
# Update /etc/pacman.d/mirrorlist using reflector
sudo reflector --sort rate --save /etc/pacman.d/mirrorlist
# Array of extensions
extarray=($(sed -e '/^#/d' -e '/^$/d' ~/templates/latex/ignored_files))
if [ "$1" == '-r' ]; then
shopt -s globstar
rm -f ${extarray[*]/#/\*\*\/\*.} # **/*.extension
else
rm -f ${extarray[*]/#/\*.} # **/*.extension
fi
exit
docker system prune
docker volume prune
rm -rf ~/.local/share/Trash
rm -rf ~/.local/share/*.xbel*
# I think this is created by KDE plasma
rm -rf ~/.local/share/baloo
sudo journalctl --vacuum-size=250M
paccache -vuk0 -r
# Top level home directory
dirs=(
src
tmp
repo
data
)
mkdir -p "${dirs[@]}"
mkdir -p ~/mnt
cd ~/mnt
dirs=(
drive
dbox
phone
android1
android2
ssd
usb
usb-guest
usbb1
usbb2
vm
)
mkdir -p "${dirs[@]}"
# Clean home of directories like Downloads, Documents, regularly created by who
# knows.
rmdir ~/Desktop ~/Downloads ~/Documents ~/Pictures ~/Videos ~/Music \
~/Templates ~/Public ~/'VirtualBox VMs' ~/mpv_slicing.log
# A script to clean tex build files
shopt -s globstar
rm **/*.aux **/*.log **/*.toc **/*.bbl **/*.fls **/*.idx **/*.ilg **/*.ind \
**/*.nlo **/*.out **/*.synctex.gz **/*.fdb_latexmk 2>&1 | grep -v \
'No such file or directory'
~/.haris/bootstrap/tangle.sh "$@"
Because this script is needed for bootstrapping my dotfiles on a new system, I also tangle it to a standalone destination that I also keep under version control.
#!/usr/bin/env -S emacs --script
(load-file (format "%s/tangle.el" (file-name-directory load-file-name)))
(haris/tangle--file-non-interactively (file-truename (elt argv 0)))
; NOTE: Although this file is kept under version control, it is tangled from
; ~/.haris/scripts.org, so don't modify it directly
~/.haris/bootstrap/tangle-all.sh "$@"
Because this script is needed for bootstrapping my dotfiles on a new system, I also tangle it to a standalone destination that I also keep under version control.
#!/usr/bin/env -S emacs --script
;; Tangle all my dotfiles. If --dest is used, then the files are tangled to
;; their final destinations under $HOME, instead of the directory returned by
;; (haris/tangle-home). Dependency installation scripts will be saved to the
;; destination returned by (haris/tangle-deps) regardless.
(setq command-switch-alist '(("--dest" . ignore)))
(setq dest (member "--dest" command-line-args))
(setq this-script-dir (file-name-directory load-file-name))
(load-file (format "%s/tangle.el" this-script-dir))
(haris/tangle-all dest :dotfiles-dir (expand-file-name
(format "%s/.." this-script-dir)))
;; vim: filetype=lisp
;; -*- mode: emacs-lisp -*-
;; NOTE: Although this file is kept under version control, it is tangled from
;; ~/.haris/scripts.org, so don't modify it directly
Usage: website-healthcheck [–ignore-success]
Options: –notify-success Show a notification even if the website status is ‘alive’
notify_success="$([ "$1" = "--notify-success" ] && echo true)"
prod_status="$(curl -L https://veracioux.me/status)"
if [ "$?" != 0 ] || [ "$prod_status" != "alive" ]; then
~/.local/bin/notify-send --urgency=critical 'ERROR' 'Production website error'
elif [ -n "$notify_success" ]; then
~/.local/bin/notify-send 'Production website' 'Status: alive'
fi
stg_status="$(curl -L https://stg.veracioux.me/status)"
if [ "$?" != 0 ] || [ "$stg_status" != "alive" ]; then
~/.local/bin/notify-send --urgency-critical 'ERROR' 'Staging website error'
elif [ -n "$notify_success" ]; then
~/.local/bin/notify-send 'Staging website' 'Status: alive'
fi
restart_if_running() {
if systemctl is-active --quiet "$1"; then
sudo systemctl restart "$1"
fi
}
if systemctl is-active --quiet protonvpn; then
sudo systemctl stop protonvpn
else
sudo systemctl start protonvpn
fi
restart_if_running stubby
restart_if_running dnsmasq
# Make vpn-notifier perform an eager query
systemctl kill --user vpn-notifier --signal=SIGUSR1
# Interval at which queries to ipinfo.io are performed periodically
default_requery_interval_sec=60
requery_time_after_sigusr1=5s
echo "PID: $$"
signal_received=''
trap 'signal_received=true' SIGUSR1
old_ip=''
while :; do
echo
echo "Querying ipinfo.io..."
response="$(curl -fs ipinfo.io)"
ip="$(jq -r .ip <<<"$response")"
country="$(jq -r .location <<<"$response")"
if [ "$ip" != "$old_ip" ]; then
yaml="$(yq -y 'del(.readme)' <<<"$response")"
echo
echo -n "IP changed:"
if [ -n "$old_ip" ]; then
notify-send "🌐 IP changed " "$yaml"
else
echo -n " (initial state)"
fi
echo
echo
echo "$(sed 's/^/ /' <<<"$yaml")"
else
echo "IP unchanged."
echo
fi
old_ip="$ip"
for x in $(seq 1 "$default_requery_interval_sec"); do
sleep 1s
if [ -n "$signal_received" ]; then
echo
echo "Received SIGUSR1, querying ipinfo.io in $requery_time_after_sigusr1..."
sleep "$requery_time_after_sigusr1"
signal_received=''
break
fi
done
done
alacritty --working-directory "$PWD" "$@"
ssh [email protected] vote "$@"
# Suspend when battery low
if acpi | grep -qP '\s1?[0-9]%' && acpi | grep -q 'Discharging'; then
timeout 0.4 ~/.local/bin/notify-send 'Low battery' 'Suspending in 5s'
sleep 5s
systemctl suspend -i
fi
cat ~/.haris/cron.d/* | crontab -
crontab -l
sudo rfkill block wifi && sudo rfkill unblock wifi
Open a GUI window in the correct workspace.
# This roundabout way is because i3-msg exec can't handle commas in arguments
script="$(mktemp)"
chmod u+x "$script"
printf "%q " "$@" > "$script"
i3-msg exec "$script"
rm -f "$script"
nvim ~/.vim/snips/"$1".snippets
Modify niceness of emacs processes to give them higher priority.
for pid in $(pgrep emacs); do
/usr/bin/renice -n -15 "$pid"
done
TODO: move to main README
I have three sets of bindings for sxhkd
:
- common; publicly available in my dotfiles repo
- private; kept in a private repo and not publicly available
- temporary; ad hoc bindings, not versioned at all
I want to be able to enable/disable each of those individually. That’s why I keep each in a separate config file. And, for each I run a separate process so I can conveniently stop/restart each by name.
exec -a sxhkd.private sxhkd -c ~/.private.sxhkdrc "$@"
exec -a sxhkd.tmp sxhkd -c ~/.tmp.sxhkdrc "$@"
My main setup is Arch Linux. Sometimes I also use other setups like Ubuntu, which sometimes have differently named packages and/or executables. I wrap the executables here so they are available under the same executable names as on Arch Linux.
By default, the adapters are archived. In order to enable them, simply unarchive them if the current PC setup is the one they are intended for.
fdfind "$@"
/usr/share/doc/pass/examples/dmenu/passmenu "$@"
# Some platforms don't provide ipython as a symlink/wrapper to ipython3
ipython3 "$@"
pycharm-professional
These scripts are used as snippets or noweb references within this org file.
Returns the second executable with the given name, looked up in execpath.
(let ((counter 0) (executable))
(locate-file name exec-path nil
(lambda (path)
(if (file-executable-p path)
(setq counter (+ counter 1)))
(> counter 1))))
;; Insert text only on macOS
(if (eq system-type 'darwin) text else)
;; Insert text only on Linux
(if (eq system-type 'darwin) text else)
(user-real-uid)
set -- "$@" $([ "$TERM" = "dumb" ] && echo --create-frame || echo '')
(user-login-name)
(getenv "HOME")