๐Ÿ’พ Archived View for gemini.omarpolo.com โ€บ dots โ€บ shells.gmi captured on 2021-12-17 at 13:26:06. Gemini links have been rewritten to link to archived content

View Raw

More Information

โฌ…๏ธ Previous capture (2021-12-03)

-=-=-=-=-=-=-

Shells

Profile

I don't know how "portable" a `.profile' can be, but let's try!

Although I'm not using acme as my go-to text editor anymore, I still like to use it from time to time, and have the rest of the plan9 ports at hand.

I manually fetched and installed the ports in `/usr/local/plan9', and need to define `$PLAN9' in order for the various tooling to work.

PLAN9=/usr/local/plan9
export PLAN9

I also tend to have an abnormal `$PATH':

PATH=$HOME/bin:$HOME/opt/emacs/bin:$HOME/opt/gcc10/bin:$HOME/go/bin:$HOME/opt/unnethack/bin:$HOME/.local/bin:$HOME/.node_modules/bin:/home/ports/infrastructure/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/X11R6/bin:/usr/local/bin:/usr/local/sbin:/usr/games:/usr/local/jdk-11/bin:$PLAN9/bin
export PATH HOME TERM

Let's split it:

Just in case I want to play with lua again:

if which luarocks-5.3 2>/dev/null >/dev/null; then
	eval "$(luarocks-5.3 path --bin)"
fi

Tell npm to install things globally in the right directory:

export npm_config_prefix=~/.node_modules

ksh doesn't have a "deafult" configuration file (like `~/.zshrc' for zsh or `~/.bashrc' for bash); instead, if called interactively it loads the file pointed by `$ENV'. Tell ksh to load `~/.kshrc' then:

export ENV=$HOME/.kshrc

An UTF-8 locale is almost mandatory, and since we're there use `en_US' as the language, even if it's not my main tongue

export LANG=en_US.UTF-8

Got is quickly becoming my favourite version control system. It should be able to load the author data from a config file, but I still keep this variable, just in case

export GOT_AUTHOR="Omar Polo <op@omarpolo.com>"

Sometimes I need to do stuff with Docker. I have a virtual machine running alpine with docker configured, and this bit here will allow docker-cli to transparently talk to the VM:

export DOCKER_HOST=ssh://op@100.64.2.3:22

I like to use `mg' as my default editor, but under `vterm' is a bit of a pain in the ass to use, since the various `C-c' and `C-x' command are intercepted by Emacs. Thus, use `vi' as the editor when `INSIDE_EMACS' (which sound strange, but for programs like `got' that spawns an editor it's the best choice I found)

if [ -z "$INSIDE_EMACS" ]; then
	VISUAL=mg
	EDITOR=mg
else
	VISUAL=vi
	EDITOR=ed
fi
export VISUAL EDITOR

less should be the default pager pretty most everywhere, but ensure that!

export MANPAGER=less

I've found a cool pager for PostgreSQL, [pspg]. It's designed explicitly for tabular data. Extra points for having some cool light themes! Tao light theme (the number 20) is my favourite.

export PSQL_PAGER='pspg -s20'

I'm using reposync to manage my local clone of the OpenBSD source tree. Technically this isn't needed, because now `/home/ports' is a checkout from `/home/cvs/...', but anyway

export CVSROOT=/home/cvs

This is just to make some command outputs a bit nicer:

export BLOCKSIZE=1m

I don't particularly like colored outputs when I'm in front of a terminal, so I tend to disable them:

export NO_COLOR='yes, please'
export CMAKE_COLOR_MAKEFILE='OFF'
export WG_COLOR_MODE=never

As an exception to the "no colors" rules, I'm trying to enabling some colors in `tog', but cautiously, and see how it goes:

export TOG_COLORS=yes
export TOG_COLOR_DIFF_MINUS=magenta
export TOG_COLOR_DIFF_PLUS=blue
export TOG_COLOR_DIFF_CHUNK_HEADER=green
export TOG_COLOR_DIFF_META=default
export TOG_COLOR_COMMIT=default
export TOG_COLOR_AUTHOR=default
export TOG_COLOR_DATE=default
export TOG_COLOR_REFS_REMOTES=red

Then disable the colors in boot (a clojure build tool I'm not using anymore) and fix its `gpg' command:

export BOOT_COLOR=no
export BOOT_GPG_COMMAND=gpg2

At some point I had a problem with leiningen that required this workaround. I've mostly switched to `deps.edn' now, but it's useful to keep this around:

export SHASUM_CMD='cksum -a sha256'

At least on OpenBSD, automake and autoconf requires these variables to be set to work

export AUTOCONF_VERSION=2.69
export AUTOMAKE_VERSION=1.16

Some time ago I played with [gerbil], a scheme dialect, and I should still have it installed.

GERBIL_HOME=/usr/local/gerbil
PATH=$PATH:$GERBIL_HOME/bin
export GERBIL_HOME PATH

I also played a bit with chicken. Don't remember why I set all these variables, but for the time being, keep 'em

#export CHICKEN_INSTALL_REPOSITORY=$HOME/.local/lib/chicken
chicken=$HOME/.chicken/11/
CHICKEN_REPOSITORY_PATH=$chicken:/usr/local/lib/chicken/11
CHICKEN_INSTALL_REPOSITORY=$chicken
CHICKEN_INCLUDE_PATH=$chicken
CHICKEN_DOC_REPOSITORY=$chicken/chicken-doc

export CHICKEN_REPOSITORY_PATH CHICKEN_INSTALL_REPOSITORY
export CHICKEN_INCLUDE_PATH CHICKEN_DOC_REPOSITORY

I don't use ripgrep, grep is fine for me, but I remember I was particularly annoyed by the format of its output. Just in case I need to use it again, here's what I did: first define an env variable that points to a configuration file:

export RIPGREP_CONFIG_PATH=$HOME/.ripgreprc

then put the following in `~/.ripgreprc':

# disable colors
--color=never
# decent output format, like grep -Hn
--vimgrep
# use smart case
--smart-case

Finally, load the specific profile for this machine, if it exists:

if [ -f "$HOME/.profile-local" ]; then
	. $HOME/.profile-local
fi

pspg

gerbil

OpenBSD ksh

OpenBSD ksh (sometimes called opdksh or oksh) is the default shell on OpenBSD, and is generally my go-to choice on other systems too. It has a good ratio of feature and simplicity.

if [ "$TERM" = dumb ]; then
	PS1='$ '
	return
fi

Enable emacs-like command editing and csh-like history expansion with `!'

set -o emacs
set -o csh-history

Talking about history, by default ksh won't store any. I don't know how I could live without it, so please enable it!

HISTCONTROL=ignoredups:ignorespace
HISTFILE=$HOME/.history
HISTSIZE=10000

`reset' doesn't work as expected inside tmux, the old output can still be consulted when scrolling. If I bother to type `reset' I want to be sure that the history was cleared, otherwise I'd use `clear'.

if [ -n "$TMUX" ]; then
	alias reset='reset && tmux clear-history'
fi

`CDPATH' is super-useful! I wrote [a post about it], also.

export CDPATH=.:$HOME/w:/home/ports:/home/ports/mystuff:$HOME/quicklisp/local-projects

I love to hate gpg! It needs some special treatments to work, and this should also fix pinentry over ssh. I'm not sure it works though, it's been a while since I connected remotely to my desktop:

export GPG_TTY=$(tty)
if [ -n "$SSH_CONNECTION" ]; then
	export PINENTRY_USER_DATA="USE_CURSES=1"
fi

The BSDs have this incredibly useful signal available, `SIGINFO', that it's a shame not to use it!

stty status ^T

I really like my prompt to be as minimal as possible. For some time I used a single colon `;' as prompt, it's really nice! At the moment though, I'm using a plan9-esque `%':

PS1='% '

a post about it

Gemini client

I got tired of trying to remember the set of flags for `nc' to talk to Gemini serves, so here we are

# gemini host [port]
#	"post" stdin to the given gemini server
gemini() {
	host=${1:?missing host}
	port=${2:-1965}
	nc -c -Tnoverify "${host}" "${port}"
}

vterm integration

`vterm' can recognize special escape sequence to pass information (like the current directory) back to Emacs.

This is an utility function to print things for vterm:

vterm_printf()
{
	if [ -n "$TMUX" ]; then
		printf '\ePtmux;\e\e]%s\007\e\\' "$1"
	elif [ "${TERM%%-*}" = "screen" ]; then
		printf '\eP\e]%s\007\e\\' "$1"
	else
		printf '\e]%s\e\\' "$1"
	fi
}

I like to improve the default vterm experience. The following will set the hostname and path every time the `$PS1' is printed, so the vterm buffer name can stay in sync, and also overrides the `cd' command:

clear()
{
	vterm_printf '51;Evterm-clear-scrollback'
	tput clear
}

vterm_set_title()
{
	printf '\033]0;%s\007' "$(hostname):$PWD"
}

vterm_prompt_end()
{
	vterm_printf "51;A$USER@$(hostname):$PWD";
}

function cd
{
	builtin cd "$@"
	vterm_set_title
}

vterm_set_title
PS1=${PS1%% }'$(vterm_prompt_end) '

but do this only when `$INSIDE_EMACS' is equal to `vterm'!

if [[ "$INSIDE_EMACS" = 'vterm' ]]; then

fi

completions

OpenBSD ksh has a limited support for programmed completions! The idea is that completions are provided via a `complete_$programname' array. It's possible to provide specific completion for the nth argument via the array `complete_$progname_$nth'.

I mean, it's not `zsh' or `fish', but it's more than enough!

Here's a completion for ssh and scp:

HOST_LIST=$(awk '/Host /{print $2}' ~/.ssh/config | xargs echo)

set -A complete_ssh -- $HOST_LIST
set -A complete_scp -- $HOST_LIST

and another simple one for kill and pkill

set -A complete_kill_1 -- -9 -HUP -INFO -KILL -TERM
set -A complete_pkill_2 -- -SIGHUP -SIGUSR1 -SIGUSR2 -SIGTERM -SIGKILL

If we're on a machine with `vmd(8)', the following will add completions for the subcommands and for the virtual machines:

if pgrep -fq /usr/sbin/vmd; then
	set -A complete_vmctl_1 -- console load reload start stop reset \
	    status send receive
	set -A complete_vmctl -- \
	    $(vmctl status | awk '!/NAME/ { printf "%s ", $NF }')
fi

Completions for ifconfig are also nice:

set -A complete_ifconfig_1 -- $(ifconfig | grep ^[a-z] | cut -d: -f1)

Add some for Got and Git:

set -A complete_got_1 --	\
	bl blame		\
	bo backout		\
	br branch		\
	ci commit		\
	co checkout		\
	cy cherrypick		\
	di diff			\
	he histedit		\
	im import		\
	in init			\
	log			\
	rb rebase		\
	ref			\
	rm remove		\
	rv revert		\
	sg stage		\
	st status		\
	tr tree			\
	ug unstage		\
	up update

set -A complete_git_1 --				\
	checkout cherry-pick clean clone commit config	\
	mpull mpush					\
	pull push					\
	status

Aliases

Some misc aliases:

alias ls="ls -F"
alias serve="python3 -m http.server"
alias ec='emacsclient -nw -c'

# colors ain't welcome here!
alias nim="nim --colors=off"

misc functions

What follows are functions that aren't big enough to be worth a whole file.

I think I stealed this two from someone. They make a backup copy of the file and then launch an editor on that, super useful when porting. The first uses `mg' and elevates the privileges with `doas'

mgdiff()
{
	if [ -z "$1" ]; then
		printf "%s\n" "USAGE: mgdiff <file>" >&2
		return
	fi
	doas cp -p "$1" "$1.orig"
	doas mg "$1"
}

The second one uses `vi' without `doas':

vdiff()
{
	if [ -z "$1" ]; then
		printf "%s\n" "USAGE: vdiff <file>" >&2
		return
	fi
	cp -p "$1" "$1.orig"
	vi "$1"
}

`hist' is a quick wrapper around `history' and `grep', to quickly search for a previous command:

hist()
{
	if [ -z "$1" ]; then
		printf "%s\n" "USAGE: hist <pattern>" >&2
		return 1
	fi

	history 0 | grep "$1"
}

`nnn' is a quick and useful file manager for the terminal. One useful feature is "auto-cd", where one can navigate the filesystem with `nnn' and upon exit, the shell will change directory to the last visited. It's pretty simple to setup, albeit probably prone to races. While there, also define some bookmarks:

export NNN_BMS="h:$HOME;t:/tmp"
export NNN_USE_EDITOR=1

bind -m '^O'='^U ncd^J^Y'

ncd()
{
	# block nesting of nnn in subshells
	if [ "${NNNLVL:-0}" -ge 1 ]; then
		echo nnn is aready running
		return
	fi

	export NNN_TMPFILE=$HOME/.config/nnn/.lastd

	nnn "$@"

	if [ -f "$NNN_TMPFILE" ]; then
		. "$NNN_TMPFILE"
		rm "$NNN_TMPFILE"
	fi
}

`goman' is a small wrapper to invoke `go doc' with a pager, which is useful when reading documentation on xterm:

goman()
{
	if [ -z "$1" ]; then
		echo "USAGE: goman terms..." >&2
		return 1
	fi

	go doc "$@" 2>&1 | ${MANPAGER:-less}
}

`rebuild_gerbil_doc' rebuilds the website with the gerbil documentation from the source shipped with the package into `/var/www/cons.local'

rebuild_gerbil_doc()
{
	rm -rf /tmp/build_gerbil_doc
	mkdir /tmp/build_gerbil_doc || return 1
	cp -R /usr/local/gerbil/doc /tmp/build_gerbil_doc/ || return 1
	cd /tmp/build_gerbil_doc/doc/
	./build.sh || return 1
	rm -rf /var/www/cons.local/*
	cp -R .vuepress/dist/* /var/www/cons.local/
}

porting-related

One of these days I'll spend some time to split and document each bit, and maybe drop unused stuff

# ports stuff
alias portsql='sqlite3 /usr/local/share/sqlports'
alias portslol='make 2>&1 | /home/ports/infrastructure/bin/portslogger .'
alias portspldc='make port-lib-depends-check'
alias portsldc='make lib-depends-check'
alias portsplif='diff -up pkg/PLIST.orig pkg/PLIST'
alias portstsilp='mv pkg/PLIST.orig pkg/PLIST'
alias portspy3plist='FLAVOR=python3 make plist'
alias portsrc='cd `make show=WRKSRC`'
alias portsfast='MAKE_JOBS=6 make'

portsdiff() { cvs diff > /home/ports/mystuff/${PWD##*/}.diff  ; less /home/ports/mystuff/${PWD##*/}.diff ;}
portslessdiff() { less /home/ports/mystuff/${PWD##*/}.diff  ; }
# portscp() { scp /home/ports/mystuff/${PWD##*/}.diff virtie:/var/www/iota/ports/ && echo https://chown.me/iota/ports/${PWD##*/}.diff ;}
portspy3() { FLAVOR="python3" make "$@" ;}
portspy3and2() { make "$@" ; FLAVOR="python3" make "$@" ;}
portspygrep() { (cd /home/ports && grep "$@" */py-*/Makefile ) ;}
portslib() { nm -g "$1" | cut -c10- | grep -e^T > /tmp/"$(pwd |xargs basename)" ;}
portsfind() { find /home/ports -iname "${1}" -exec grep -iH ${2} {} \; ;}
portsgrep() { ( cd /home/ports && grep "$@" */*/Makefile */*/*/Makefile ) ;}

alias mup="make update-patches"
alias pfast="MAKE_JOBS=7 make"
alias m="make"
alias mpldc="make port-lib-depends-check"

pclear()
{
	doas find /home/ports/packages/ -iname "*${1:?}*" -delete
	doas find /home/ports/plist/ -iname "*${1:?}*" -delete
}

rc

Although it's not my interactive shell, I do like plan9' rc.

My configuration file is pretty small:

prompt=('% ' '')
user=$USER
home=$HOME

fn % { $* }
fn git { env git --no-pager $* }

I use the following for the plumber, although it probably can be improved:

addr=':(#?[0-9]+)'
protocol='(https?|ftp|file|gopher|mailto|news|nntp|telnet|wais)'
domain='[a-zA-Z0-9_@]+([.:][a-zA-Z0-9_@]+)*/?[a-zA-Z0-9_?,%#~&/\-]+'
file='([:.][a-zA-Z0-9_?,%#~&/\-]+)*'

# open http urls.  data regexps is the same for file plus :
type is text
data matches $protocol://$domain$file
plumb to web
plumb start web $0

# RFC's from one of the nicer-looking repositories.
type is text
data matches 'RFC:([0-9]+)'
plumb to web
plumb start browser https://tools.ietf.org/html/rfc$1

# open python error message
type is text
data matches ' *File "([a-zA-Z0-9_\.\/]*)", line ([0-9]*).*'
plumb to edit
arg isfile $1
data set $file
attr add addr=$2
plumb client $editor

# open pdf with xdg-open
type is text
data matches '[a-zA-Zยก-๏ฟฟ0-9_\-./]+'
data matches '([a-zA-Zยก-๏ฟฟ0-9_\-./]+)\.(ps|PS|eps|EPS|pdf|PDF|dvi|DVI)'
arg isfile $0
plumb to postscript
plumb start xdg-open $file

# show git log
type is text
data matches 'commit ([a-z0-9]*)'
arg isdir .
data set $dir
plumb start sh -c 'cd '$dir'; git show '$1' | 9p write acme/new/body'

# show git log
type is text
data matches 'commit ([a-z0-9]*)'
arg isdir .
data set $dir
plumb start sh -c 'cd '$dir'; git show '$1' | 9p write acme/new/body'

# git pull
type is text
data matches '.*[pP][uU][lL][lL].*#([0-9]*)'
arg isdir .
data set $dir
plumb start sh -c 'cd '$dir'; browser $(git remote get-url origin | sed "s/\.git//")/pull/'$1

# git issue
type is text
data matches '[iI][sS][sS][uU][eE] #([0-9]*)'
arg isdir .
data set $dir
plumb start sh -c 'cd '$dir'; browser $(git remote get-url origin | sed "s/\.git//")/issues/'$1

# git issue
type is text
data matches '.*fix.*#([0-9]*)'
arg isdir .
data set $dir
plumb start sh -c 'cd '$dir'; browser $(git remote get-url origin | sed "s/\.git//")/issues/'$1

SQLite

SQLite has a configuration file that gets executed every time is launched. I like to change the default glyph for the `NULL' value

.nullvalue 'โŠฅ'

and enable the `box' mode. This is kinda new, so it may not work in some older version

.mode box

It looks like this:

.mode box
select 42 as response;

,----

| โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”

| โ”‚ response โ”‚

| โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ค

| โ”‚ 42 โ”‚

| โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

`----

psql

By default psql renders `NULL' values as empty strings. This makes it harder to "see" if a column is `NULL' or an empty string, so change the default `NULL' glyph:

\pset null 'โŠฅ'

I also use to connect to databases to different hosts, so to be extra sure I made `psql' print the connection info right away:

\conninfo

Scripts

acmerc

I use the following script to launch acme in all its glory.

#!/usr/bin/env rc

. $home/lib/profile

if (~ $PLAN9 '') {
	echo '$PLAN9 is not defined!'
	exit 1
}

NAMESPACE=/tmp/ns.$user.$pid

SHELL=rc
PAGER=nobs
MANPAGER=nobs
EDITOR=editinacme
VISUAL=editinacme

mkdir -p $"NAMESPACE

plumber
fontsrv &
fontsrvpid=$apid

font=/mnt/font/GoMono/10a/font
FONT=/mnt/font/InputSans-Regular/10a/font

$PLAN9/bin/acme -a -f $font -F $FONT $* &
acmepid=$apid

{
	sleep 1
	winid=1
	exec acmeeval 'autoacme '$home'/bin/acmeconfig'
} &
acmeevalpid=$apid

wait $acmepid

kill $acmeevalpid
kill $fontsrvpid

wait # just in case

rm -rf $"NAMESPACE

browser

The `browser' script is my default browser. It launches the correct browser depending on what is currently running

#!/bin/sh

if pgrep firefox >/dev/null 2>&1; then
	exec firefox "$1"
fi

if pgrep iridium >/dev/null 2>&1; then
	exec iridium "$1"
fi

exec firefox "$1"

clbin

Posts its input to clbin

#!/bin/sh

exec curl -F 'clbin=<-' https://clbin.com

menu

This generates a menu for a `dmenu' like program. In particular, it uses my own mymenu.

#!/bin/ksh

a-menu() {
	mymenu -f 'Go Mono-11' -l vertical -p '% ' \
	       -W 50% -H 30% -P 10 -x center -y center \
	       -C '#ffffea' -c '#000' -T '#ffffea' \
	       -t '#000' -S '#000' -s '#fff' -b 3 \
	       -a
}

# pass
p() {
	prefix=${PASSWORD_STORE_DIR:-~/.password-store}
	typeit=${1:-no}

	sleep 1
	p=$(find "$prefix" -type f -iname '*.gpg' | \
		sort | \
		sed -e 's/\.gpg$//' -e "s,^$prefix/,," | \
		a-menu)
	if [ $? -eq 0 ]; then
		if [ "$typeit" = yes ]; then
			pass show "$p" | { IFS= read -r pass; printf %s "$pass"; } |
				xdotool type --clearmodifiers --file -
		else
			pass show --clip "$password"
		fi
	fi
}

# exec
e() {
	if ! x=$(a-menu); then
		return
	elif [ "$x" = "pass" ]; then
		p yes
	elif [ "$x" = "pass copy" ]; then
		p nope
	elif [ "$x" = "keep" ]; then
		exec keepassxc
	else
		exec $x
	fi
}

(

	echo audacity
	echo blender
	echo chrome
	echo dino
	echo emacs
	echo emacsclient -c
	echo firefox
	echo gajim
	echo gimp
	echo godot
	echo inkscape
	echo iridium
	echo keep
	echo lagrange
	echo libreoffice
	echo links -g -mode 800x600
	echo lmms
	echo luakit
	echo lxappearance
	echo mumble
	echo netsurf-gtk3
	echo obs
	echo pass
	echo pass copy # not "copy pass" so it's after pass
	echo pixelorama
	echo poedit
	echo spectral
	echo tor-browser
	echo xfe
	echo zathura

) | e

record

Record, as the name suggest, records a portion of the screen to a file.

#!/bin/ksh

if ! s=$(slop -f "%x %y %w %h"); then
	exit 1
fi

set -A s -- $s

x=${s[0]}
y=${s[1]}
w=${s[2]}
h=${s[3]}

exec ffmpeg -y \
	-f x11grab \
	-s ${w}x${h} \
	-framerate 30 \
	-i $DISPLAY+${x},${y} \
	${1:?missing output file}

stumpwm-wrapper

I like to jump between stumpwm and cwm, but I haven't found a way to do `exec cwm' from lisp, hence I'm using this script from `cwm' to switch to `stumpwm'.

#!/bin/sh

stumpwm
exec cwm

xdg-open

Time ago I decided to just stop even trying to tame `xdg-open' and fix the problem at the root, that is, by getting rid of it.

I have an `xdg-open' scripts that implements the rules that *I* want, not some coincidences decided by the order in which the package were installed.

<2021-06-20 Sun> I've installed `pdf-tools', so there isn't any need for zathura.

#!/bin/sh

case "$@" in
	*://*)		exec browser "$@" ;;
	*jpg|*jpeg)	exec gpicview "$@" ;;
	*mp4|*mkv)	exec mpv "$@" ;;
	*m4a)		exec mpv --force-window --lavfi-complex='[aid1] asplit [ao] [v] ; [v] showwaves=mode=line:split_channels=1 [vo]' "$@" ;;
	*svg)		exec inkscape "$@" ;;
	*core)		;; # do nothing
	*png)		exec gpicview "$@" ;;
	*gif)		exec gpicview "$@" ;;
	*webp)		exec gpicview "$@" ;;
	*)		exec emacsclient -c "$@" ;;
esac