dotfiles

configuration files for shell, text editor, graphical environment, etc.
git clone git://src.adamsgaard.dk/dotfiles # fast
git clone https://src.adamsgaard.dk/dotfiles.git # slow
Log | Files | Refs | README | LICENSE Back to index

plumb (8883B)


      1 #!/bin/sh
      2 
      3 calendar="$HOME/.calendar/calendar"
      4 prefix=""
      5 
      6 if command -v hurl >/dev/null 2>&1; then
      7 	fetcher="hurl"
      8 elif command -v curl >/dev/null 2>&1; then
      9 	fetcher="curl -sL"
     10 else
     11 	die 'could not find hurl or curl'
     12 fi
     13 
     14 show_help() {
     15 	printf "usage: %s [OPTIONS] [FILE|URL ..]" "${0##*/}"
     16 	echo "will open FILE or URL."
     17 	echo "If no FILE or URL is specified, they are expected as stdin."
     18 	echo
     19 	echo "OPTIONS are one or more of the following:"
     20 	echo "   -h		show this message"
     21 	echo "   -v		show verbose information"
     22 	echo "   -w		do not end program before child processes are finished"
     23 	echo "   --		do not consider any following args as options"
     24 }
     25 
     26 die() {
     27 	printf '%s: error: %s\n' "${0##*/}" "$1" >&2
     28 	exit 1
     29 }
     30 
     31 detach() {
     32 	if [ "$wait" = 1 ]; then
     33 		"$@"
     34 	else
     35 		nohup "$@" >/dev/null 2>&1 &
     36 	fi
     37 }
     38 
     39 regexmatch() {
     40 	printf '%s' "$1" | grep -E -q "$2"
     41 }
     42 
     43 regeximatch() {
     44 	printf '%s' "$1" | grep -i -E -q "$2"
     45 }
     46 
     47 termopen() {
     48 	if [ -z "$TERM" -o "$TERM" = linux -o "$TERM" = dumb ]; then
     49 		detach $TERMINAL "$@"
     50 	else
     51 		"$@"
     52 	fi
     53 }
     54 
     55 fetch() {
     56 	_o="$(mktemp)"
     57 
     58 	if ! regeximatch "$1" '^ftp:' ; then
     59 		$prefix $fetcher "$1" > "$_o" 2> /tmp/plumb_err
     60 	else
     61 		$prefix curl -sL "$1" > "$_o" 2> /tmp/plumb_err
     62 	fi
     63 	if [ $? -ne 0 ]; then
     64 		msg="could not fetch $1\\n\\n$(cat /tmp/plumb_err)"
     65 		printf '%s\n' "$msg" >&2
     66 		notify "${0##*/}" "$msg"
     67 	fi
     68 	printf '%s' "$_o"
     69 }
     70 
     71 annotate_date_string() {
     72 	sed 's/^\([0-9][0-9][0-9][0-9]\)\([0-1][0-9]\)\([0-3][0-9]\)/\1-\2-\3 /
     73 		s/T\([0-2][0-9]\)\([0-5][0-9]\)\([0-5][0-9]\)/\1:\2:\3/'
     74 }
     75 
     76 handle_calendar_invite() {
     77 	starttime="$(grep -E "DTSTART.*" "$1" | sed 's/.*://' | \
     78 		tail -1 | annotate_date_string )"
     79 	endtime="$(grep -E "DTEND.*" "$1" | sed 's/.*://' | \
     80 		tail -1 | annotate_date_string )"
     81 	tzone="$(grep -E "DTEND.*TZID" "$1" | sed 's/.*TZID=\(.*\):.*/\1/' )"
     82 	summary="$(grep -E "SUMMARY[;:].*:" "$1" | sed 's/[^:]*: *//' )"
     83 	location="$(grep -E "LOCATION[;:].*:" "$1" | sed 's/[^:]*: *//' )"
     84 
     85 	printf 'start time: %s\n' "$starttime"
     86 	printf 'end time:   %s\n' "$endtime"
     87 	printf 'time zone:  %s\n' "$tzone"
     88 	printf 'location:   %s\n' "$location"
     89 	printf 'summary:    %s\n' "$summary"
     90 
     91 	printf 'add to calendar? [y/N] '
     92 	read -r
     93 	case "$REPLY" in
     94 		Y|y|YES|Yes|yes|myes)
     95 			cd "$(dirname "$calendar")" && git pull
     96 			month="$(printf '%s' "$starttime" | \
     97 				sed 's/.*-\([0-1][0-9]\)-[0-3][0-9].*/\1/')"
     98 			day="$(printf '%s' "$starttime" | \
     99 				sed 's/.*-[0-1][0-9]-\([0-3][0-9]\).*/\1/')"
    100 			hour_start="$(printf '%s' "$starttime" | \
    101 				sed 's/.* \([0-2][0-9]\):[0-5][0-9].*/\1/')"
    102 			min_start="$(printf '%s' "$starttime" | \
    103 				sed 's/.* [0-2][0-9]:\([0-5][0-9]\).*/\1/')"
    104 			hour_end="$(printf '%s' "$endtime" | \
    105 				sed 's/.* \([0-2][0-9]\):[0-5][0-9].*/\1/')"
    106 			min_end="$(printf '%s' "$endtime" | \
    107 				sed 's/.* [0-2][0-9]:\([0-5][0-9]\).*/\1/')"
    108 			printf '%s/%s\t%s:%s-%s:%s %s (%s, %s)\n' "$month" "$day" \
    109 				"$hour_start" "$min_start" "$hour_end" "$min_end" \
    110 				"$summary" "$location" "$tzone"\
    111 				>> "$calendar"
    112 			rsync -a "$calendar" adamsgaard.dk:~/.calendar;;
    113 		*) ;;
    114 	esac
    115 }
    116 
    117 handle_file_type() {
    118 	mime_type="$(file -ib "$1")"
    119 	if [ "$verbose" = 1 ]; then
    120 		printf 'target is a file with mime type "%s"\n' "$mime_type"
    121 	fi
    122 	case "$mime_type" in
    123 		video/*|\
    124 		audio/*|\
    125 		application/ogg|\
    126 		application/octet-stream)
    127 			mpv "$1";;
    128 		text/html)
    129 			detach $BROWSER "$1";;
    130 		text/troff)
    131 			termopen mandoc -l "$1";;
    132 		text/*)
    133 			if [ "$(file -b "$1")" = "vCalendar calendar file" ]; then
    134 				handle_calendar_invite "$1"
    135 			elif regexmatch "$1" '[A-z0-9]\.[0-9]$'; then
    136 				termopen mandoc -l "$1"
    137 			else
    138 				termopen $PAGER "$1"
    139 			fi;;
    140 		*shellscript*)
    141 			$EDITOR "$1";;
    142 		application/*html*)
    143 			detach $BROWSER "$1";;
    144 		application/pdf*|application/postscript*|image/vnd.djvu)
    145 			detach zathura "$1";;
    146 		application/*document*|\
    147 		application/msword*|\
    148 		application/*-excel*|\
    149 		application/*powerpoint*|\
    150 		application/zip)
    151 			detach libreoffice "$1";;
    152 		image/svg*)
    153 			detach inkscape "$1";;
    154 		image/*)
    155 			detach sxiv -a -s f "$1";;
    156 		*)
    157 			die "file type $mime_type is not supported"
    158 	esac
    159 }
    160 
    161 handle_uri() {
    162 	t="$1"
    163 	if [ "$verbose" = 1 ]; then
    164 		printf 'target is not a file\n'
    165 	fi
    166 	if regeximatch "$t" '\.onion'; then
    167 		prefix=torsocks
    168 	else
    169 		prefix=
    170 	fi
    171 
    172 	case "$t" in
    173 		*scholar.google.*)
    174 			t="$(printf '%s' "$t" | \
    175 				sed 's/.*scholar_url?url\=//;s/&hl\=.*//')";;
    176 	esac
    177 
    178 	case "$t" in
    179 		*10.5194/tc-*)
    180 			nf="$(printf %s "$t" | awk -F- '{print NF}')"
    181 			h="https://tc.copernicus.org"
    182 			if [ "$nf" -eq 4 ]; then
    183 				t="$(printf '%s' "$t" | awk -F- -v h="$h" '{print h"/articles/"$2"/"$3"/"$4"/tc-"$2"-"$3"-"$4".pdf"}')"
    184 			elif [ "$nf" -eq 3 ]; then
    185 				t="$(printf '%s' "$t" | awk -F- -v h="$h" '{print h"/preprints/tc-"$2"-"$3"/tc-"$2"-"$3".pdf"}')"
    186 			fi;;
    187 		*the-cryosphere-discuss.net/tc-*/)
    188 			a="$(printf '%s' "$t" | \
    189 				sed 's/.*\(tc-[0-9]*-[0-9]*\).*/\1/')"
    190 			t="${t}${a}.pdf";;
    191 		*the-cryosphere.net/*/*/*/)
    192 			v="$(printf '%s' "$t" | \
    193 				sed 's,.*/\([0-9][0-9]\)/.*,\1,')"
    194 			n="$(printf '%s' "$t" | \
    195 				sed 's,.*/[0-9][0-9]/\([0-9][0-9][0-9][0-9]\)/.*,\1,')"
    196 			y="$(printf '%s' "$t" | \
    197 				sed 's,.*/\([0-9][0-9][0-9][0-9]\)/.*,\1,')"
    198 			t="${t}tc-${v}-${n}-${y}.pdf";;
    199 		*tc.copernicus.org/articles/*/)
    200 			t="$(printf '%s' "$t" | awk -F/ -v h="$t" '{print h"tc-"$5"-"$6"-"$7".pdf"}')";;
    201 		*gmd.copernicus.org/articles/*/)
    202 			t="$(printf '%s' "$t" | awk -F/ -v h="$t" '{print h"gmd-"$5"-"$6"-"$7".pdf"}')";;
    203 	esac
    204 
    205 	if regexmatch "$t" '^(0|1|3|7|9|g|I|h)' && \
    206 		  [ "$(printf '%s' "$t" | awk -F'\t' '{print NF}')" -eq 4 ]; then
    207 		# construct uri from gopher item
    208 		type="$(printf '%s' "$t" | cut -c1)"
    209 		path="$(printf '%s' "$t" | cut -d'	' -f2)"
    210 		host="$(printf '%s' "$t" | cut -d'	' -f3)"
    211 		port="$(printf '%s' "$t" | cut -d'	' -f4)"
    212 		t="gopher://${host}:${port}/${type}${path}"
    213 	elif regexmatch "$t" '^\[(0|1|3|7|9|g|I|h)\|.*\|.*\|.*\|[0-9]+\]$'; then
    214 		type="$(printf '%s' "$t" | cut -c2)"
    215 		path="$(printf '%s' "$t" | cut -d'|' -f3)"
    216 		host="$(printf '%s' "$t" | cut -d'|' -f4)"
    217 		port="$(printf '%s' "$t" | cut -d'|' -f5 | sed 's,\],,')"
    218 		t="gopher://${host}:${port}/${type}${path}"
    219 	fi
    220 	if regeximatch "$t" '^radio://'; then
    221 		$prefix mpv --force-window=no "http${t##radio}"
    222 	elif regeximatch "$t" '^ssh://'; then
    223 		termopen $prefix ssh "$t"
    224 	elif regeximatch "$t" '\.(mp4|mkv|webm|avi|ogv|m3u|pls)$'; then
    225 		if regeximatch "$t" '^gophers://'; then
    226 			$prefix $fetcher "$t" | mpv -
    227 		else
    228 			detach $prefix mpv "$t"
    229 		fi
    230 	elif regeximatch "$t" '\.(mp3|ogg|m3u|wav|opus)$'; then
    231 		$prefix mpv "$(fetch "$t")"
    232 	elif regeximatch "$t" '\.(png|jpg|jpeg|tif|bmp|gif)$'; then
    233 		$prefix sxiv -a -s f "$(fetch "$t")"
    234 	elif regeximatch "$t" '\.svg$'; then
    235 		_o="$(mktemp)"
    236 		convert svg:- png:- <"$(fetch "$t")" > "$_o"
    237 		sxiv -s f "$_o"
    238 	elif regeximatch "$t" '\.webp$'; then
    239 		$prefix sxiv -a -s f "$(unwebp -e "$(fetch "$t")")"
    240 	elif regeximatch "$t" '\.(pdf|ps|epub|djvu)$'; then
    241 		$prefix zathura "$(fetch "$t")"
    242 	elif regeximatch "$t" '(smbc-comics\.com|xkcd\.com|jspowerhour\.com|explosm\.net|patchfriday\.com)'; then
    243 		cd /tmp && detach comic "$t"
    244 	elif regexmatch "$t" '^doi.*10\.[0-9]*\/'; then
    245 		t="${t#doi://}"
    246 		t="${t#doi:}"
    247 		detach $prefix $BROWSER "https://doi.org/${t}"
    248 	elif regexmatch "$t" '\.(txt|patch|diff|rst|md|vtt|vtv)$'; then
    249 		f="$(fetch "$t")"
    250 		termopen $PAGER "$f"
    251 	elif regeximatch "$t" '^gopher://'; then
    252 		termopen $prefix sacc "$t"
    253 	elif regeximatch "$t" '^gophers://'; then
    254 		termopen $prefix plumb "$(fetch "$t")"
    255 	elif regeximatch "$t" '[A-z]\.[0-9]$'; then
    256 		if [ -z "$TERM" ]; then
    257 			detach $TERMINAL mandoc -l "$(fetch "$t")"
    258 		else
    259 			mandoc -l "$(fetch "$t")"
    260 		fi
    261 	elif regeximatch "$t" '(youtube\.|youtu\.be|vimeo\.com|invidious|invidio\.us|invidious\.snopyta\.org)'; then
    262 		detach $prefix mpv "$t"
    263 	elif regeximatch "$t" '^(http|https)://' || \
    264 		regeximatch "$t" '\.webp$'; then
    265 
    266 		if regeximatch "$t" '\.zoom\.us/'; then
    267 			export ENABLE_WASM=1
    268 			detach chrome --enable-wasm "$t"
    269 		else
    270 			detach $prefix $BROWSER "$t"
    271 		fi
    272 	else
    273 		die "unknown target type: $t"
    274 	fi
    275 }
    276 
    277 handle_target() {
    278 	t="$1"
    279 	if [ "$verbose" = 1 ]; then
    280 		printf 'target: %s\n' "$t"
    281 	fi
    282 	if [ -d "$t" ]; then
    283 		if [ "$verbose" = 1 ]; then
    284 			printf 'target is a directory\n'
    285 		fi
    286 		termopen cd $t; $SHELL
    287 	elif [ -e "$t" ]; then
    288 		handle_file_type "$t"
    289 	else
    290 		handle_uri "$t"
    291 	fi
    292 	s="$?"
    293 	if [ "$s" -ne 0 ]; then
    294 		if [ "$verbose" = 1 ]; then
    295 			echo "child process error"
    296 		fi
    297 		exit "$s"
    298 	fi
    299 }
    300 
    301 verbose=0
    302 wait=0
    303 while :; do
    304 	case "$1" in
    305 		-h)
    306 			show_help
    307 			exit 0
    308 			;;
    309 		-v)
    310 			verbose=1
    311 			;;
    312 		-w)
    313 			wait=1
    314 			;;
    315 		--) # end all options
    316 			shift
    317 			break
    318 			;;
    319 		-?*)
    320 			die 'error: Unknown option specified'
    321 			;;
    322 		*)  # No more options
    323 			break
    324 	esac
    325 	shift
    326 done
    327 
    328 if [ $# -lt 1 ]; then
    329 	while IFS='' read -r f || [ -n "$f" ]; do
    330 		handle_target "$f"
    331 	done < "${1:-/dev/stdin}"
    332 else
    333 	for f in "$@"; do
    334 		handle_target "$f"
    335 	done
    336 fi