aboutsummaryrefslogtreecommitdiff
path: root/shells.org
blob: 25625bce1bcb5888023f8e5f108ea66272fa61ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
#+TITLE: Shells
#+HTML_HEAD: <link rel="stylesheet" type="text/css" href="solarized-light.css" />

* Profile
  :PROPERTIES:
  :header-args: :tangle ~/.profile
  :END:

  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.
  #+begin_src sh
    PLAN9=/usr/local/plan9
    export PLAN9
  #+end_src

  I also tend to have an abnormal =$PATH=:
  #+begin_src sh
    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:/usr/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
  #+end_src

  Let's split it:
  - =$HOME/bin= is for my personal scripts, it needs to take
    precedence over anything else
  - =$HOME/opt/*= contains various stuff I compile from source, like emacs
  - =$HOME/.local/bin= is XDG stuff I'm practically forced to use
  - =$HOME/.node_modules/bin= is for node
  - =/usr/ports/infrastructure/bin= holds some handy port tools
  - the rest is the usual =$PATH= on OpenBSD, with the addition of
    java and plan9 at the end.

  Just in case I want to play with lua again:

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

  Tell npm to install things globally in the right directory:

  #+begin_src sh
    export npm_config_prefix=~/.node_modules
  #+end_src

  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:

  #+begin_src sh
    export ENV=$HOME/.kshrc
  #+end_src

  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

  #+begin_src sh
    export LANG=en_US.UTF-8
  #+end_src

  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

  #+begin_src sh
    export GOT_AUTHOR="Omar Polo <op@omarpolo.com>"
  #+end_src

  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:

  #+begin_src sh
    export DOCKER_HOST=ssh://op@100.64.2.3:22
  #+end_src

  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)

  #+begin_src sh
    if [ -z "$INSIDE_EMACS" ]; then
	    VISUAL=mg
	    EDITOR=mg
    else
	    VISUAL=vi
	    EDITOR=ed
    fi
    export VISUAL EDITOR
  #+end_src

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

  #+begin_src sh
    export MANPAGER=less
  #+end_src

  I've found a cool pager for PostgreSQL, [[https://github.com/okbob/pspg][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.

  #+begin_src sh
    export PSQL_PAGER='pspg -s20'
  #+end_src

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

  #+begin_src sh
    export CVSROOT=/home/cvs
  #+end_src

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

  #+begin_src sh
    export BLOCKSIZE=1m
  #+end_src

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

  #+begin_src sh
    export NO_COLOR='yes, please'
    export CMAKE_COLOR_MAKEFILE='OFF'
    export WG_COLOR_MODE=never
  #+end_src

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

  #+begin_src sh
    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
  #+end_src

  Then disable the colors in boot (a clojure build tool I'm not using
  anymore) and fix its =gpg= command:
#+begin_src sh
  export BOOT_COLOR=no
  export BOOT_GPG_COMMAND=gpg2
#+end_src

  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:

  #+begin_src sh
    export SHASUM_CMD='cksum -a sha256'
  #+end_src

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

  #+begin_src sh
    export AUTOCONF_VERSION=2.69
    export AUTOMAKE_VERSION=1.16
  #+end_src

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

    #+begin_src sh
      GERBIL_HOME=/usr/local/gerbil
      PATH=$PATH:$GERBIL_HOME/bin
      export GERBIL_HOME PATH
    #+end_src

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

    #+begin_src sh
      #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
    #+end_src

    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:

    #+begin_src sh
      export RIPGREP_CONFIG_PATH=$HOME/.ripgreprc
    #+end_src

    then put the following in =~/.ripgreprc=:

    #+begin_src conf :tangle ~/.ripgreprc
      # disable colors
      --color=never
      # decent output format, like grep -Hn
      --vimgrep
      # use smart case
      --smart-case
    #+end_src

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

    #+begin_src sh
      if [ -f "$HOME/.profile-local" ]; then
	      . $HOME/.profile-local
      fi
    #+end_src

* OpenBSD ksh
  :PROPERTIES:
  :header-args: :tangle ~/.kshrc
  :END:

  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.

  #+begin_src sh
    if [ "$TERM" = dumb ]; then
	    PS1='$ '
	    return
    fi
  #+end_src

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

  #+begin_src sh
    set -o emacs
    set -o csh-history
  #+end_src

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

  #+begin_src sh
    HISTCONTROL=ignoredups:ignorespace
    HISTFILE=$HOME/.history
    HISTSIZE=10000
  #+end_src

  =CDPATH= is super-useful!  I wrote [[https://www.omarpolo.com/post/enjoying-cdpath.html][a post about it]], also.

  #+begin_src sh
    export CDPATH=.:$HOME/w:/usr/ports:/usr/ports/mystuff:$HOME/quicklisp/local-projects
  #+end_src

  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:

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

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

  #+begin_src sh
    stty status ^T
  #+end_src

  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 =%=:

  #+begin_src sh
    PS1='% '
  #+end_src

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

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

** 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:

   #+begin_src sh
     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
     }
   #+end_src

   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:

   #+NAME: when-vterm
   #+begin_src sh :tangle no
     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) '
   #+end_src

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

   #+begin_src sh :noweb strip-export
     if [[ "$INSIDE_EMACS" = 'vterm' ]]; then
	     <<when-vterm>>
     fi
   #+end_src

** 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:

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

     set -A complete_ssh -- $HOST_LIST
     set -A complete_scp -- $HOST_LIST
   #+end_src

   and another simple one for kill and pkill

   #+begin_src sh
     set -A complete_kill_1 -- -9 -HUP -INFO -KILL -TERM
     set -A complete_pkill_2 -- -SIGHUP -SIGUSR1 -SIGUSR2 -SIGTERM -SIGKILL
   #+end_src

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

   #+begin_src sh
     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
   #+end_src

   Completions for ifconfig are also nice:

   #+begin_src sh
     set -A complete_ifconfig_1 -- $(ifconfig | grep ^[a-z] | cut -d: -f1)
   #+end_src

   Add some for Got and Git:

   #+begin_src sh
     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
   #+end_src

** Aliases

   Some misc aliases:

   #+begin_src sh
     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"
   #+end_src

** 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=

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

   #+end_src

   The second one uses =vi= without =doas=:

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

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

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

	     history 0 | grep "$1"
     }
   #+end_src

   =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:

   #+begin_src sh
     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
     }
   #+end_src

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

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

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

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

   #+begin_src sh
     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/
     }
   #+end_src

** porting-related

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

   #+begin_src sh
     # ports stuff
     alias portsql='sqlite3 /usr/local/share/sqlports'
     alias portslol='make 2>&1 | /usr/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 > /usr/ports/mystuff/${PWD##*/}.diff  ; less /usr/ports/mystuff/${PWD##*/}.diff ;}
     portslessdiff() { less /usr/ports/mystuff/${PWD##*/}.diff  ; }
     # portscp() { scp /usr/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 /usr/ports && grep "$@" */py-*/Makefile ) ;}
     portslib() { nm -g "$1" | cut -c10- | grep -e^T > /tmp/"$(pwd |xargs basename)" ;}
     portsfind() { find /usr/ports -iname "${1}" -exec grep -iH ${2} {} \; ;}
     portsgrep() { ( cd /usr/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 /usr/ports/packages/ -iname "*${1:?}*" -delete
	     doas find /usr/ports/plist/ -iname "*${1:?}*" -delete
     }
   #+end_src

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

  My configuration file is pretty small:

  #+begin_src sh :tangle ~/lib/profile
    prompt=('% ' '')
    user=$USER
    home=$HOME

    fn % { $* }
    fn git { env git --no-pager $* }
  #+end_src

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

  #+begin_src conf :tangle ~/lib/plumbing
    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
  #+end_src

* SQLite
    :PROPERTIES:
    :header-args: :tangle ~/.sqliterc
    :END:

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

  #+begin_src conf
    .nullvalue '⊥'
  #+end_src

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

  #+begin_src conf
    .mode box
  #+end_src

  It looks like this:

  #+begin_src sqlite :tangle no :db "" :results verbatim :exports both
    .mode box
    select 42 as response;
  #+end_src

  #+RESULTS:
  | ┌──────────┐ |
  | │ response │ |
  | ├──────────┤ |
  | │ 42       │ |
  | └──────────┘ |

* psql
    :PROPERTIES:
    :header-args: :tangle ~/.psqlrc
    :END:

  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:

  #+begin_src conf
    \pset null '⊥'
  #+end_src

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

  #+begin_src conf
    \conninfo
  #+end_src

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

   #+begin_src sh :tangle ~/bin/acmerc :tangle-mode (identity #o755)
     #!/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
   #+end_src
** browser
   The =browser= script is my default browser.  It launches the
   correct browser depending on what is currently running

   #+begin_src sh :tangle ~/bin/browser :tangle-mode (identity #o755)
     #!/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"
   #+end_src

** clbin
   Posts its input to clbin

   #+begin_src sh :tangle ~/bin/clbin :tangle-mode (identity #o755)
     #!/bin/sh

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

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

   #+begin_src sh :tangle ~/bin/menu :tangle-mode (identity #o755)
     #!/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 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

     ) | e
   #+end_src

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

   #+begin_src sh :tangle ~/bin/record :tangle-mode (identity #o755)
     #!/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}
   #+end_src

** 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=.

   #+begin_src sh :tangle ~/bin/stumpwm-wrapper :tangle-mode (identity #o755)
     #!/bin/sh

     stumpwm
     exec cwm
   #+end_src

** 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.

   #+begin_src sh :tangle ~/bin/xdg-open :tangle-mode (identity #o755)
     #!/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
   #+end_src