many improvements, a few small fixes
authorIan Kelling <ian@iankelling.org>
Thu, 12 Mar 2026 16:58:39 +0000 (12:58 -0400)
committerIan Kelling <ian@iankelling.org>
Thu, 12 Mar 2026 16:58:39 +0000 (12:58 -0400)
15 files changed:
big.sqliterc [new file with mode: 0644]
brc
brc2
fast.sqliterc [new file with mode: 0644]
filesystem/etc/systemd/system/hist-catcher.service [new file with mode: 0644]
filesystem/usr/local/bin/abrowser-profile-plus [new file with mode: 0755]
filesystem/usr/local/bin/btrbk-run
fsf-script-lib
g
hist-catcher [new file with mode: 0755]
machine_specific/frodo/filesystem/etc/btrbk/n123.conf [new file with mode: 0644]
pkgs
subdir_files/.config/konsolerc
subdir_files/.local/share/konsole/ian.keytab
subdir_files/.local/share/konsole/profileian.profile

diff --git a/big.sqliterc b/big.sqliterc
new file mode 100644 (file)
index 0000000..58051fb
--- /dev/null
@@ -0,0 +1,15 @@
+-- persistent from initial setup
+-- PRAGMA journal_mode = WAL;
+
+-- on a live system:
+-- 0 = OFF 1 = NORMAL 2 = FULL
+
+.output /dev/null
+PRAGMA synchronous = NORMAL;
+PRAGMA journal_size_limit = 6144000;
+PRAGMA temp_store = MEMORY;
+PRAGMA cache_size = 262144;
+PRAGMA soft_heap_limit = 0;
+PRAGMA mmap_size = 268435456;
+PRAGMA auto_vacuum = INCREMENTAL;
+.output stdout
diff --git a/brc b/brc
index b1b3fe32bd24016d23879abb2f97c5a84fe1dba0..91ded246e5ef2b14756ed315bc3068d19cd805bc 100644 (file)
--- a/brc
+++ b/brc
@@ -2347,30 +2347,38 @@ k() {
   local grep_out1 grep_out2 histf1 histf2
   local -a lines1 lines2
   histf1=${HISTFILE:-~/.bash_history}
-  grep_out1=$(grep -iP --binary-files=text "$*" $histf1  | tail -n 80) || [[ $? == 1 ]]
+  grep_out1=$(grep -iP --binary-files=text "$*" $histf1  | tail -n 80|tac) || [[ $? == 1 ]]
   if [[ $EUID == 0 ]]; then
-    histf2="/home/iank/.bh"
-    grep_out2=$(grep -iP --binary-files=text "$*" $histf2  | tail -n 80) || [[ $? == 1 ]]
+    # condition because with that hisfile, root and user have the same one.
+    if [[ $HISTFILE != /r/dot/rbh ]]; then
+      histf2="/home/iank/.bh"
+      if [[ -s $histf2 ]]; then
+        grep_out2=$(grep -iP --binary-files=text "$*" $histf2  | tail -n 80|tac) || [[ $? == 1 ]]
+      fi
+    fi
   elif sudo -nv 2>/dev/null; then
     histf2="/root/.bh"
-    grep_out2=$(sudo grep -iP --binary-files=text "$*" $histf2  | tail -n 80) || [[ $? == 1 ]]
+    if ! sudo test -s $histf2; then
+      histf2="/root/.bash_history"
+    fi
+    grep_out2=$(sudo grep -iP --binary-files=text "$*" $histf2  | tail -n 80|tac) || [[ $? == 1 ]]
   fi
   mapfile -t lines1 <<<"$grep_out1"
   mapfile -t lines2 <<<"$grep_out2"
   if [[ $grep_out1 && $grep_out2 ]]; then
     # in this case, just insert a chunk of secondary results in the middle.
     if (( ${#lines1[@]} > 20 )); then
-      printf "%s\n" "${lines1[@]:20}"; printf "############ ^ %s #############\n"  $histf1
-      printf "%s\n" "${lines2[@]::20}"; printf "############ ^ %s #############\n"  $histf2
-      printf "%s\n" "${lines1[@]::20}"
+      printf "%s\n" "${lines1[@]:20}"|tac; printf "############ ^ %s #############\n"  $histf1
+      printf "%s\n" "${lines2[@]::20}"|tac; printf "############ ^ %s #############\n"  $histf2
+      printf "%s\n" "${lines1[@]::20}"|tac
     else
-      printf "%s\n" "${lines2[@]}"; printf "########## ^ %s #########\n" $histf2;
-      printf "%s\n" "${lines1[@]}"
+      printf "%s\n" "${lines2[@]}"|tac; printf "########## ^ %s #########\n" $histf2;
+      printf "%s\n" "${lines1[@]}"|tac
     fi
   elif [[ $grep_out2 ]]; then
-    printf "%s\n" "${lines2[@]}"; printf "### ^ %s\n" $histf2
+    printf "%s\n" "${lines2[@]}"|tac; printf "### ^ %s\n" $histf2
   elif [[ $grep_out1 ]]; then
-    printf "%s\n" "${lines1[@]}"
+    printf "%s\n" "${lines1[@]}"|tac
   fi
 }
 
@@ -2750,6 +2758,13 @@ r() {
   #  exit "$@" 2>/dev/null
 }
 
+# Like sed -i, but running whatever command you want with sponge.
+ri() {
+  # shellcheck disable=SC2124 # false positive
+  local file="${@: -1}" # last arg
+  "$@" | sponge "$file"
+}
+
 # rsync with nice defaults.
 # scp is a bit insecure and deprecated.
 sp() {
@@ -3851,10 +3866,10 @@ fndwc() {
   for d; do
     count=$(find -L "$d" -type f -printf a | wc -c)
     total+=$count
-    e "$count $d "
+    printf "%'12d   %s\n" $count "$d"
   done
   if (( total > count )); then
-    e "total $total"
+    printf "%'12d   %s\n" $total sum
   fi
 }
 
@@ -4823,6 +4838,45 @@ iptI() { ${*/ -I/ -C/} 2>/dev/null || "$@"; }
 iptA() { ${*/ -A/ -C/} 2>/dev/null || "$@"; }
 
 
+### how I calculated lswc-avg (at least for a btrfs filesystem):
+# f() { local d ; local -i size s=0 i=0; while read -r _ _ _ _ size _ _ _ d; do [[ -d $d ]] || continue; i+=$(lswc $d); s+=$size; done < <(ls -lA); calc $s/$i; e s $s  i $i; }; f
+# 26.452
+# s 22720934 i 858939
+
+lswc-avg() {
+  local -i size fcount sum=0
+  for i in $(stat -c %s "$@"); do
+    fcount=$(( i / 26 ))
+    sum+=$fcount
+    printf "%'12d   %s\n" $fcount "$1"
+    shift
+  done
+  if (( sum > fcount )); then
+    printf "%'12d   %s\n" $sum sum
+  fi
+
+}
+
+# usage $0  PID...
+maxpri() {
+  # we try process group (-g, -P) to hit related processes. If this fails, fall back to pid: (-p, -p)
+  s renice -20 -g "$@"
+  s ionice -c1 -n0 -P "$@"
+}
+
+# usage $0  PID...
+minpri() {
+  s renice 19 -g "$@"
+  s ionice -c3 -P "$@"
+}
+# Prefix for launching a command:
+maxpri="s nice -n-40 ionice -c1 -n0"
+minpri="nice -n40 ionice -c3"
+
+sql() {
+  sqlite3 "$@"
+  }
+
 # * stuff that makes sense to be at the end
 
 # note, if we unset IFS, that will mess up completion scripts which
diff --git a/brc2 b/brc2
index b862fbbebcb89442d566568536ce0efb6b2a8a63..3491ce7a9c98485adcd24ea38827fcf1b46db0ee 100644 (file)
--- a/brc2
+++ b/brc2
@@ -615,7 +615,7 @@ _iki-convert() {
       if [[ $url == *.mdwn ]]; then
         url="${url%.mdwn}/"
       fi
-      j echo "$url"
+      jc echo "$url"
       ;;
   esac
 }
@@ -1624,114 +1624,6 @@ batp() {
 }
 
 
-fdup() {
-  local -A installed updated
-  local p
-  local -a fdroid_pkgs
-
-  # List of apps to install/update
-  # Create from existing manually installed apps by doing
-  # fdroidcl update
-  # fdroidcl search -i, then manually removing
-  # automatically installed/preinstalled apps
-
-  #
-  # # my attempt at recovering from boot loop:
-  # # in that case, boot to recovery (volume up, home button, power, let go of power after samsun logo)
-  # # then
-  # mount /dev/block/mmcblk0p12 /data
-  # cd /data
-  # find -iname '*appname*'
-  # rm -rf FOUND_DIRS
-  # usually good enough to just rm -rf /data/app/APPNAME
-  #
-  # currently broken:
-  # # causes replicant to crash
-  # org.quantumbadger.redreader
-  # org.kde.kdeconnect_tp
-
-  # not broke, but wont work without gps
-  #com.zoffcc.applications.zanavi
-  # not broke, but not using atm
-  #com.nutomic.syncthingandroid
-  # # doesn\'t work on replicant
-  #net.sourceforge.opencamera
-  #
-  fdroid_pkgs=(
-    net.mullvad.mullvadvpn
-    org.schabi.newpipe
-    io.github.subhamtyagi.lastlauncher
-    io.anuke.mindustry
-    com.biglybt.android.client
-    de.marmaro.krt.ffupdater
-    me.ccrama.redditslide
-    org.fedorahosted.freeotp
-    at.bitfire.davdroid
-    com.alaskalinuxuser.justnotes
-    com.artifex.mupdf.viewer.app
-    com.danielkim.soundrecorder
-    com.fsck.k9
-    com.ichi2.anki
-    com.jmstudios.redmoon
-    com.jmstudios.chibe
-    org.kde.kdeconnect_tp
-    com.notecryptpro
-    com.termux
-    cz.martykan.forecastie
-    de.danoeh.antennapod
-    de.blinkt.openvpn
-    de.marmaro.krt.ffupdater
-    eu.siacs.conversations
-    free.rm.skytube.oss
-    im.vector.alpha # riot
-    info.papdt.blackblub
-    me.tripsit.tripmobile
-    net.gaast.giggity
-    net.minetest.minetest
-    net.osmand.plus
-    org.isoron.uhabits
-    org.linphone
-    org.gnu.icecat
-    org.smssecure.smssecure
-    org.yaaic
-    sh.ftp.rocketninelabs.meditationassistant.opensource
-  )
-  # https://forum.xda-developers.com/android/software-hacking/wip-selinux-capable-superuser-t3216394
-  # for maru,
-  #me.phh.superuser
-
-
-  # tried putting this in go buildscript cronjob,
-  # but it failed with undefined: os.UserCacheDir. I expect its due to
-  # an environment variable missing, but its easier just to stick it here.
-  m go get -u mvdan.cc/fdroidcl || return 1
-  m fdroidcl update
-  if fdroidcl search -u | grep ^org.fdroid.fdroid; then
-    fdroidcl install org.fdroid.fdroid
-    sleep 5
-    m fdroidcl update
-  fi
-  for p in $(fdroidcl search -i| grep -o "^\S\+"); do
-    installed[$p]=true
-  done
-  for p in $(fdroidcl search -u| grep -o "^\S\+"); do
-    updated[$p]=false
-  done
-  for p in ${fdroid_pkgs[@]}; do
-    if ! ${installed[$p]:-false}; then
-      m fdroidcl install $p
-      # sleeps are just me being paranoid since replicant has a history of crashing when certain apps are installed
-      sleep 5
-    fi
-  done
-  for p in ${!installed[@]}; do
-    if ! ${updated[$p]:-true}; then
-      m fdroidcl install $p
-      sleep 5
-    fi
-  done
-}
-
 firefox-default-profile() {
   local key value section
   key=Default
@@ -2579,18 +2471,13 @@ sudm() {
   fi
 }
 
-_in_rootns() {
-  local tmps a b
-  tmps=$(s readlink /proc/1/ns/net /proc/$$/ns/net)
-  { read -r a; read -r b; } <<<"$tmps"
-  if [[ ! $b ]]; then
-    e "error: _in_rootns failed to get current netns"
-    return 1
-    fi
-  if [[ $a != "$b" ]]; then
-    e "error: _in_rootns: not in the root netns. return 1"
-    return 1
-  fi
+# Returns 1 if we are not in the base mount namespace aka root mnt ns.
+mntns1-p() {
+  s stat -Lc %i /proc/{1,self}/ns/mnt|uniq -d|read
+}
+# Like mntns1-p except with network ns.
+netns1-p() {
+  s stat -Lc %i /proc/{1,self}/ns/net|uniq -d|read
 }
 
 mns-setup() {
@@ -2617,7 +2504,7 @@ mns-no-setup() {
   local ns
   ns=$1
   shift
-  _in_rootns
+  mntns1-p
 
   m sudm /usr/bin/nsenter --mount=/root/mount_namespaces/$ns "$@"
 }
@@ -2626,7 +2513,7 @@ mns() { # mount namespace
   local ns
   ns=$1
   shift
-  _in_rootns
+  mntns1-p
 
   mns-setup $ns
   m sudm /usr/bin/nsenter --mount=/root/mount_namespaces/$ns "$@"
@@ -2635,7 +2522,7 @@ mns() { # mount namespace
 
 mnsr() { # mns run (as normal user)
   local ns pre_check tmpf user alt_user=false tmps
-  _in_rootns
+  mntns1-p
 
   user=$USER
   while [[ $1 ]]; do
@@ -2674,10 +2561,11 @@ mnsr() { # mns run (as normal user)
   m sudm nsenter --mount=/root/mount_namespaces/$ns sudo -u $user -i "${final_args[@]}"
 }
 
-mnsd() { # mount namespace + systemd network namespace
+# Run cmd or shell in named mount namespace + systemd network namespace
+mnsd() {
   local ns unit user tmpf pre_check pid alt_user=false
   local -a final_args
-  _in_rootns
+  mntns1-p
 
   ## begin command line args ##
   user=$USER
@@ -2720,7 +2608,7 @@ mnsd() { # mount namespace + systemd network namespace
 
 mnsnonetroot() {
   ns=$1
-  _in_rootns
+  mntns1-p
   lomh
   if ! s ip netns list | grep -Fx nonet &>/dev/null; then
     s ip netns add nonet
@@ -2731,7 +2619,7 @@ mnsnonetroot() {
 
 mnsnonet() {
   ns=$1
-  _in_rootns
+  mntns1-p
   lomh
   if ! s ip netns list | grep -Fx nonet &>/dev/null; then
     s ip netns add nonet
@@ -2744,7 +2632,7 @@ mnsnonet() {
 lom() {
   # l = the loopback device
   local l base
-  _in_rootns
+  mntns1-p
   # get sudo pass cached right away
   if ! sudo -nv 2>/dev/null; then
     sudo -v
@@ -3253,7 +3141,7 @@ jc() {
 xc() {
   xclip -r -selection clipboard "$@"
 }
-# echo copy.  e text to copy | ec
+# echo copy.  e text to copy | ec
 ec() {
   pee "xclip -r -selection clipboard" cat
 }
@@ -3712,14 +3600,13 @@ enn() {
   fi
 }
 
-# Check that network ns of pid $1 really exists, like joins-namespace-of-check
+# Usage: ns-exists PID
+# Check that network ns of PID exists (is not the default namespace), like joins-namespace-of-check
 ns-exists() {
-  local service_ns default_ns pid
+  local pid
   pid="$1"
   [[ $pid ]]
-  service_ns=$(s readlink /proc/$pid/ns/net)
-  default_ns=$(s readlink /proc/1/ns/net)
-  [[ $service_ns && $service_ns != "$default_ns" ]]
+  ! s stat -Lc %i /proc/{1,$pid}/ns/net|uniq -d|read
 }
 
 # Get pid of systemd service
@@ -3768,7 +3655,7 @@ sdnbash() { # systemd namespace bash
   fi
   unit=$1
   pid=$(servicepid $unit)
-  m sudo nsenter -t $pid -n -m sudo -u $USER -i bash
+  m sudo nsenter -t $pid -n $(mntns1-p && e -m) sudo -u $USER -i bash
 }
 
 sdnbashroot() { # systemd namespace bash as root
@@ -3779,7 +3666,7 @@ sdnbashroot() { # systemd namespace bash as root
   fi
   unit=$1
   pid=$(servicepid $unit)
-  m sudo nsenter -t $pid -n -m bash
+  m sudo nsenter -t $pid -n $(mntns1-p && e -m) bash
 }
 
 
@@ -3810,7 +3697,10 @@ sdncmd() {
   else
     final_args=(bash -c ". $tmpf")
   fi
-  m sudo nsenter -t $pid -n -m sudo -u $user -i "${final_args[@]}"
+
+  # We use the mount namespace to use a non-systemd dns but doing it
+  # twice doesn't work.
+  m sudo nsenter -t $pid -n $(mntns1-p && e -m) sudo -u $user -i "${final_args[@]}"
 }
 
 sdncmdroot() { # systemd namespace root command
@@ -4196,7 +4086,8 @@ rem() {
       break
     fi
   done
-  paths="/b/"
+  # 2nd path is git ignored but i want to include it.
+  paths="/b/ /b/fai/fai/config/files/usr/local/"
   find $paths -not \( -name .svn -prune -o -name .git -prune \
        -o -name .hg -prune -o -name .editor-backups -prune \
        -o -name .undo-tree-history -prune \) 2>/dev/null | grep -iP --color=auto -- "$*" ||:
@@ -4207,32 +4098,6 @@ reml() { # rem with limit to 5 matches per file
   rem "$@"
 }
 
-rep() {
-  local paths
-  local -a opts
-  if [[ ! $1 ]]; then
-    echo rem: missing argument >&2
-    return 1
-  fi
-  for arg; do
-    if [[ $arg == -* ]]; then
-      opts+=("$1")
-      shift
-    else
-      break
-    fi
-  done
-  paths="/p/c/ /p/bin/ /p/profanity-config/ /b/bash_unpublished/ /c/ /f/s/fsf/ /f/backup-scripts/ /f/gluestick/"
-  find $paths -not \( -name .svn -prune -o -name .git -prune \
-       -o -name .hg -prune -o -name .editor-backups -prune \
-       -o -name .undo-tree-history -prune \) 2>/dev/null | grep -iP --color=auto -- "$*" ||:
-  rgv $local_rgv_args "${opts[@]}" -- "$*" $paths /a/t.org /p/w.org ||:
-}
-repl() { # rem with limit to 5 matches per file
-  local local_rgv_args="-m 5"
-  rem "$@"
-}
-
 
 # re on common fsf files
 ref() {
@@ -5111,3 +4976,73 @@ EOF
   cd
   rm -r "$tmpdir"
 }
+
+# Given a dirname, make the first letter the basis for adding another dir in front,
+# a..t. d20e = directory / 20 echoed
+# eg: adir -> a/adir
+d20e() {
+  local l dir=$1
+  l=${dir::1}
+  l=${l,,}
+  if [[ $l < a ]]; then
+    l=q
+  elif [[ $l > t ]]; then
+    l=t
+  fi
+  echo "$l/$dir"
+}
+# Alt interface: set $d to the dir.
+d20() {
+  local dir=$1
+  l=${dir::1}
+  l=${l,,}
+  if [[ $l < a ]]; then
+    l=q
+  elif [[ $l > t ]]; then
+    l=t
+  fi
+  d="$l/$dir"
+}
+# l20e: letter of 20 echoed
+l20e() {
+  local l dir=$1
+  l=${dir::1}
+  l=${l,,}
+  if [[ $l < a ]]; then
+    l=q
+  elif [[ $l > t ]]; then
+    l=t
+  fi
+  echo "$l/$dir"
+}
+# Alt interface: set $l to the letter.
+l20() {
+  local dir=$1
+  l=${dir::1}
+  l=${l,,}
+  if [[ $l < a ]]; then
+    l=q
+  elif [[ $l > t ]]; then
+    l=t
+  fi
+}
+
+# Perl implementation:
+#
+# my $l = fc(substr($udir, 0, 1));
+# $l = 'q' if ($l lt 'a');
+# $l = 't' if ($l gt 't');
+#
+### SQL implementation is in json2sql.
+
+maxpri-audio() {
+  local tmps
+  local -a pids
+  tmps=$(pgrep -f ^/usr/bin/pipewire)
+  e pipewire pids: $tmps
+  pids=( $tmps )
+  tmps=$(pgrep '^mpv$')
+  e mpv pids: $tmps
+  pids+=( $tmps )
+  maxpri ${pids[@]}
+}
diff --git a/fast.sqliterc b/fast.sqliterc
new file mode 100644 (file)
index 0000000..8bc9da3
--- /dev/null
@@ -0,0 +1,6 @@
+.output /dev/null
+-- note: this causes sqlite cli to print "memory"
+PRAGMA journal_mode = MEMORY;
+PRAGMA synchronous = OFF;
+PRAGMA temp_store = MEMORY;
+.output stdout
diff --git a/filesystem/etc/systemd/system/hist-catcher.service b/filesystem/etc/systemd/system/hist-catcher.service
new file mode 100644 (file)
index 0000000..5738a18
--- /dev/null
@@ -0,0 +1,18 @@
+[Unit]
+Description=bash remote history
+StartLimitIntervalSec=0
+After=local-fs.target
+
+[Service]
+Type=simple
+ExecStart=/usr/local/bin/hist-catcher
+IOSchedulingClass=idle
+CPUSchedulingPolicy=idle
+User=iank
+Group=iank
+Restart=always
+RestartSec=600
+
+
+[Install]
+WantedBy=graphical.target
diff --git a/filesystem/usr/local/bin/abrowser-profile-plus b/filesystem/usr/local/bin/abrowser-profile-plus
new file mode 100755 (executable)
index 0000000..be100c9
--- /dev/null
@@ -0,0 +1,51 @@
+#!/bin/bash
+# I, Ian Kelling, follow the GNU license recommendations at
+# https://www.gnu.org/licenses/license-recommendations.en.html. They
+# recommend that small programs, < 300 lines, be licensed under the
+# Apache License 2.0. This file contains or is part of one or more small
+# programs. If a small program grows beyond 300 lines, I plan to switch
+# its license to GPL.
+
+# Copyright 2024 Ian Kelling
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+#     http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# note this is duplicated in i3-abrowser
+
+if ! test "$BASH_VERSION"; then echo "error: shell is not bash" >&2; exit 1; fi
+shopt -s inherit_errexit 2>/dev/null ||: # ignore fail in bash < 4.4
+set -eE -o pipefail
+trap 'echo "$0:$LINENO:error: \"$BASH_COMMAND\" exit status: $?, PIPESTATUS: ${PIPESTATUS[*]}" >&2' ERR
+
+
+
+# -allow-downgrade good enough?
+#rm -f {/p/c/firefox-main-profile,/p/c/firefox-main-profile,/p/c/firefox-vpn2-profile,/mnt/z/firefox-vpn-profile}/compatibility.ini
+
+# --allow-downgrade
+# ^ useful option for when the browser refuses to run, but it always
+# causes a new browser window to open, even if normally it would open a
+# new tab
+
+profile="$1"
+shift
+
+if pgrep -f "^/usr/lib/abrowser/abrowser --new-instance -P ${profile}$" &>/dev/null; then
+  if (( $# )); then
+    abrowser -P $profile --new-tab "$@"
+  else
+    abrowser -P $profile
+  fi
+else
+  abrowser --new-instance -P $profile &>/dev/null &
+fi
index f4088d4bbcc57f3b1867a266167054bb44880112..f3bcf1a4fbf62fe86817b380166c5e45ee59c782 100755 (executable)
@@ -265,6 +265,9 @@ incremental_prefs sao:1
 # btrbk -l trace -v dryrun
 
 rate_limit $rate_limit
+
+send_protocol 2
+send_compressed_data yes
 EOF
 
   if $incremental_strict; then
index b7d3fc8fe0a3c39f81dccab6142eb15cfbc8a030..7660e5e3704980e6d3c1b745882cd177e59cbf83 100644 (file)
@@ -139,30 +139,37 @@ m() {
   "$@"
 }
 
-# usage: mq COMMAND...
-#
-# echo COMMAND if verbose=true.
-# Then run it. Like m, but more quietly.
-mq() {
-  # shellcheck disable=SC2154
-  if [[ $verbose == true ]]; then
-    printf "+ %s\n" "$*" >&2
-  fi
+
+# mb, maybe. echo args if they fail.
+mb() {
+  local evars ret=0
   while [[ $1 == *=* ]]; do
     declare -x "$1"
+    evars+="$1 "
     shift
   done
-  "$@"
+  "$@" || ret=$?
+  if (( ret )); then
+    printf "error: exit code $ret from: %s\n" "$evars$*" >&2
+  fi
+  return $ret
 }
 
-# mb, maybe. echo args if they fail.
-mb() {
+# usage: mq COMMAND...
+#
+# echo COMMAND if verbose=true & if it fails.
+# Like m, but more quietly.
+mq() {
   local evars ret=0
   while [[ $1 == *=* ]]; do
     declare -x "$1"
     evars+="$1 "
     shift
   done
+  # shellcheck disable=SC2154
+  if [[ $verbose == true ]]; then
+    printf "+ %s\n" "$evars$*" >&2
+  fi
   "$@" || ret=$?
   if (( ret )); then
     printf "error: exit code $ret from: %s\n" "$evars$*" >&2
@@ -170,6 +177,7 @@ mb() {
   return $ret
 }
 
+
 # usage: e [MESSAGE...]
 #
 # echo MESSAGE, but use printf so it is safe from options.
diff --git a/g b/g
index 1d6c0d33956a77a7623817cb32e1b772ccc2949c..713735c4a5936e3d4d6c0526a1c4fb9084b77a36 100755 (executable)
--- a/g
+++ b/g
@@ -79,5 +79,5 @@ g() {
   fi
 }
 
-#echo "$*" >>/tmp/g.log
+echo "$*" >>/tmp/g.log
 g "$@"
diff --git a/hist-catcher b/hist-catcher
new file mode 100755 (executable)
index 0000000..ad8c963
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/bash
+# I, Ian Kelling, follow the GNU license recommendations at
+# https://www.gnu.org/licenses/license-recommendations.en.html. They
+# recommend that small programs, < 300 lines, be licensed under the
+# Apache License 2.0. This file contains or is part of one or more small
+# programs. If a small program grows beyond 300 lines, I plan to change
+# to a recommended GPL license.
+
+# Copyright 2025 Ian Kelling
+
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+
+#     http://www.apache.org/licenses/LICENSE-2.0
+
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+cmd_regex='[[:alnum:]]'
+
+while true; do
+  cmd=$(nc -l 36661) || sleep 1
+  if [[ ! $cmd =~ $cmd_regex ]]; then
+    continue
+  fi
+  history -s "$cmd" && history -a ||:
+done
diff --git a/machine_specific/frodo/filesystem/etc/btrbk/n123.conf b/machine_specific/frodo/filesystem/etc/btrbk/n123.conf
new file mode 100644 (file)
index 0000000..b54f8ee
--- /dev/null
@@ -0,0 +1,39 @@
+transaction_syslog local7
+
+lockfile                   /var/lock/btrbk-n3.lock
+
+timestamp_format long-iso
+
+snapshot_create onchange
+
+snapshot_preserve 18h 14d 8w 12m
+snapshot_preserve_min 2d
+snapshot_dir btrbk
+
+target_preserve 18h 14d 8w 12m
+target_preserve_min 2d
+
+rate_limit no
+
+volume /mnt/n3
+subvolume dia
+subvolume dib
+subvolume dic
+subvolume did
+subvolume die
+subvolume dif
+subvolume dig
+subvolume dih
+subvolume dii
+subvolume dij
+subvolume dik
+subvolume dil
+subvolume dim
+subvolume din
+subvolume dio
+subvolume dip
+subvolume diq
+subvolume dir
+subvolume dis
+subvolume dit
+subvolume dlapi
diff --git a/pkgs b/pkgs
index 4dc41bac71822bcdcaa06b24e39f47f69c8b7584..da852e18efe5237281f23493c3ae7b44a4282d19 100644 (file)
--- a/pkgs
+++ b/pkgs
@@ -179,6 +179,7 @@ p3=(
   libsereal-perl
   libsmart-comments-perl
   libstatistics-descriptive-perl
+  libclass-dbi-sqlite-perl
   perl-tk
 
 
index d81c9ce3e3b4151fb8e5631dcdb02daf4af36d05..d3847adc772bc847041d0f5e41921a582b60ece9 100644 (file)
@@ -24,7 +24,7 @@ ShowMenuBarByDefault=false
 2 screens: XPosition=2
 2 screens: YPosition=2
 3840x2160 screen: Height=2156
-3840x2160 screen: Width=1276
+3840x2160 screen: Width=3836
 3840x2160 screen: XPosition=1282
 3840x2160 screen: YPosition=2
 DP-0=DP-0
@@ -33,8 +33,9 @@ DP-1-1 eDP-1=DP-1-1
 DP-1-3 eDP-1=eDP-1
 HDMI-1 eDP-1=eDP-1
 HDMI-2 eDP-1=HDMI-2
-MenuBar=Disabled
-State=AAAA/wAAAAD9AAAAAQAAAAAAAAAAAAAAAPwCAAAAAvsAAAAiAFEAdQBpAGMAawBDAG8AbQBtAGEAbgBkAHMARABvAGMAawAAAAAA/////wAAAXYA////+wAAABwAUwBTAEgATQBhAG4AYQBnAGUAcgBEAG8AYwBrAAAAAAD/////AAABFgD///8AAAd6AAAIagAAAAQAAAAEAAAACAAAAAj8AAAAAQAAAAIAAAACAAAAFgBtAGEAaQBuAFQAbwBvAGwAQgBhAHIAAAAAAP////8AAAAAAAAAAAAAABwAcwBlAHMAcwBpAG8AbgBUAG8AbwBsAGIAYQByAAAAAIz/////AAAAAAAAAAA=
+MenuBar=Enabled
+RestorePositionForNextInstance=false
+State=AAAA/wAAAAD9AAAAAQAAAAAAAAAAAAAAAPwCAAAAAvsAAAAiAFEAdQBpAGMAawBDAG8AbQBtAGEAbgBkAHMARABvAGMAawAAAAAA/////wAAATUA////+wAAABwAUwBTAEgATQBhAG4AYQBnAGUAcgBEAG8AYwBrAAAAAAD/////AAAA5QD///8AAA78AAAIVgAAAAQAAAAEAAAACAAAAAj8AAAAAQAAAAIAAAACAAAAFgBtAGEAaQBuAFQAbwBvAGwAQgBhAHIAAAAAAP////8AAAAAAAAAAAAAABwAcwBlAHMAcwBpAG8AbgBUAG8AbwBsAGIAYQByAAAAAAD/////AAAAAAAAAAA=
 ToolBarsMovable=Disabled
 eDP-1=eDP-1
 
index efcb6f82800fbbf842e3070ec53982c15c3c782c..21385d79da7c93eb36519f22ed81766d0930ac73 100644 (file)
 keyboard "ian"
-key Backspace+Ctrl : "\b"
-key Backspace-Ctrl : "\x7f"
-key Backtab+Ansi : "\E[Z"
-key Backtab-Ansi : "\t"
-key Backtab+Ctrl+Ansi : "\E[27;6;9~"
-key Backtab+Ctrl-Ansi : "\t"
-key Clear+KeyPad : "\E[E"
 key .+Ctrl+AppScreen : "\E[4c"
-key Del+AnyModifier : "\E[3;*~"
-key Del-AnyModifier : "\E[3~"
-key Del+KeyPad : "\E[3~"
-key Down-Shift+Ansi+AnyModifier : "\E[1;*B"
-key Down-Shift+Ansi-AppCursorKeys-AnyModifier : "\E[B"
-key Down-Shift+Ansi+AppCursorKeys-AnyModifier : "\EOB"
-key Down-Shift-Ansi : "\EB"
-key Down+Shift+AppScreen : "\E[1;*B"
-key Down+Shift-AppScreen : ScrollLineDown
-key Down-Shift+KeyPad+Ansi-AppCursorKeys : "\E[B"
-key Down-Shift+KeyPad+Ansi+AppCursorKeys : "\EOB"
-key End+AnyModifier : "\E[1;*F"
-key End-AppCursorKeys-AnyModifier : "\E[F"
-key End+AppCursorKeys-AnyModifier : "\EOF"
-key End+KeyPad-AppCursorKeys : "\E[F"
-key End+KeyPad+AppCursorKeys : "\EOF"
+key Space+Ctrl : "\x00"
+key I+Ctrl+AppScreen : "\E[4d"
+key M+Ctrl+AppScreen : "\E[4e"
+key Home+Shift-AppScreen : ScrollUpToTop
+key Home+KeyPad+AppCursorKeys : "\EOH"
+key Home+KeyPad-AppCursorKeys : "\E[H"
+key Home+AppCursorKeys-AnyModifier : "\EOH"
+key Home-AppCursorKeys-AnyModifier : "\E[H"
+key Home+AnyModifier : "\E[1;*H"
 key End+Shift-AppScreen : ScrollDownToBottom
-key Enter-NewLine : "\r"
-key Enter+NewLine : "\r\n"
+key End+KeyPad+AppCursorKeys : "\EOF"
+key End+KeyPad-AppCursorKeys : "\E[F"
+key End+AppCursorKeys-AnyModifier : "\EOF"
+key End-AppCursorKeys-AnyModifier : "\E[F"
+key End+AnyModifier : "\E[1;*F"
+key Left-Shift+KeyPad+Ansi+AppCursorKeys : "\EOD"
+key Left-Shift+KeyPad+Ansi-AppCursorKeys : "\E[D"
+key Left+Shift+AppScreen : "\E[1;*D"
+key Left-Shift-Ansi : "\ED"
+key Left-Shift+Ansi+AppCursorKeys-AnyModifier : "\EOD"
+key Left-Shift+Ansi-AppCursorKeys-AnyModifier : "\E[D"
+key Left-Shift+Ansi+AnyModifier : "\E[1;*D"
+key Up-Shift+KeyPad+Ansi+AppCursorKeys : "\EOA"
+key Up-Shift+KeyPad+Ansi-AppCursorKeys : "\E[A"
+key Up+Shift-AppScreen : ScrollLineUp
+key Up+Shift+AppScreen : "\E[1;*A"
+key Up-Shift-Ansi : "\EA"
+key Up-Shift+Ansi+AppCursorKeys-AnyModifier : "\EOA"
+key Up-Shift+Ansi-AppCursorKeys-AnyModifier : "\E[A"
+key Up-Shift+Ansi+AnyModifier : "\E[1;*A"
+key Right-Shift+KeyPad+Ansi+AppCursorKeys : "\EOC"
+key Right-Shift+KeyPad+Ansi-AppCursorKeys : "\E[C"
+key Right+Shift+AppScreen : "\E[1;*C"
+key Right-Shift-Ansi : "\EC"
+key Right-Shift+Ansi+AppCursorKeys-AnyModifier : "\EOC"
+key Right-Shift+Ansi-AppCursorKeys-AnyModifier : "\E[C"
+key Right-Shift+Ansi+AnyModifier : "\E[1;*C"
+key Down-Shift+KeyPad+Ansi+AppCursorKeys : "\EOB"
+key Down-Shift+KeyPad+Ansi-AppCursorKeys : "\E[B"
+key Down+Shift-AppScreen : ScrollLineDown
+key Down+Shift+AppScreen : "\E[1;*B"
+key Down-Shift-Ansi : "\EB"
+key Down-Shift+Ansi+AppCursorKeys-AnyModifier : "\EOB"
+key Down-Shift+Ansi-AppCursorKeys-AnyModifier : "\E[B"
+key Down-Shift+Ansi+AnyModifier : "\E[1;*B"
+key PgUp-Shift+KeyPad : "\E[5~"
+key PgUp+Shift-AppScreen : ScrollPageUp
+key PgUp-Shift-AnyModifier : "\E[5~"
+key PgUp-Shift+AnyModifier : "\E[5;*~"
+key PgDown-Shift+KeyPad : "\E[6~"
+key PgDown+Shift-AppScreen : ScrollPageDown
+key PgDown-Shift-AnyModifier : "\E[6~"
+key PgDown-Shift+AnyModifier : "\E[6;*~"
+key Clear+KeyPad : "\E[E"
 key Esc : "\E"
-key F10+AnyModifier : "\E[21;*~"
+key Tab-Shift : "\t"
+key Tab+Shift-Ansi : "\t"
+key Tab+Shift+Ansi : "\E[Z"
+key Tab+Ctrl-Ansi : "\t"
+key Tab+Ctrl+Ansi : "\E[27;5;9~"
+key Backtab+Ctrl-Ansi : "\t"
+key Backtab+Ctrl+Ansi : "\E[27;6;9~"
+key Backtab-Ansi : "\t"
+key Backtab+Ansi : "\E[Z"
+key Backspace-Ctrl : "\x7f"
+key Backspace+Ctrl : "\b"
+key Return-Shift+NewLine : "\r\n"
+key Return-Shift-NewLine : "\r"
+key Return+Shift : "\EOM"
+key Enter+NewLine : "\r\n"
+key Enter-NewLine : "\r"
+key Ins+KeyPad : "\E[2~"
+key Ins-AnyModifier : "\E[2~"
+key Ins+AnyModifier : "\E[2;*~"
+key Del+KeyPad : "\E[3~"
+key Del-AnyModifier : "\E[3~"
+key Del+AnyModifier : "\E[3;*~"
+key F9-AnyModifier : "\E[20~"
+key F9+AnyModifier : "\E[20;*~"
 key F10-AnyModifier : "\E[21~"
-key F11+AnyModifier : "\E[23;*~"
+key F10+AnyModifier : "\E[21;*~"
 key F11-AnyModifier : "\E[23~"
-key F12+AnyModifier : "\E[24;*~"
+key F11+AnyModifier : "\E[23;*~"
 key F12-AnyModifier : "\E[24~"
-key F1+AnyModifier : "\EO*P"
+key F12+AnyModifier : "\E[24;*~"
 key F1-AnyModifier : "\EOP"
-key F2+AnyModifier : "\EO*Q"
+key F1+AnyModifier : "\EO*P"
 key F2-AnyModifier : "\EOQ"
-key F3+AnyModifier : "\EO*R"
+key F2+AnyModifier : "\EO*Q"
 key F3-AnyModifier : "\EOR"
-key F4+AnyModifier : "\EO*S"
+key F3+AnyModifier : "\EO*R"
 key F4-AnyModifier : "\EOS"
-key F5+AnyModifier : "\E[15;*~"
+key F4+AnyModifier : "\EO*S"
 key F5-AnyModifier : "\E[15~"
-key F6+AnyModifier : "\E[17;*~"
+key F5+AnyModifier : "\E[15;*~"
 key F6-AnyModifier : "\E[17~"
-key F7+AnyModifier : "\E[18;*~"
+key F6+AnyModifier : "\E[17;*~"
 key F7-AnyModifier : "\E[18~"
-key F8+AnyModifier : "\E[19;*~"
+key F7+AnyModifier : "\E[18;*~"
 key F8-AnyModifier : "\E[19~"
-key F9+AnyModifier : "\E[20;*~"
-key F9-AnyModifier : "\E[20~"
-key Home+AnyModifier : "\E[1;*H"
-key Home-AppCursorKeys-AnyModifier : "\E[H"
-key Home+AppCursorKeys-AnyModifier : "\EOH"
-key Home+KeyPad-AppCursorKeys : "\E[H"
-key Home+KeyPad+AppCursorKeys : "\EOH"
-key Home+Shift-AppScreen : ScrollUpToTop
-key I+Ctrl+AppScreen : "\E[4d"
-key Ins+AnyModifier : "\E[2;*~"
-key Ins-AnyModifier : "\E[2~"
-key Ins+KeyPad : "\E[2~"
-key Left-Shift+Ansi+AnyModifier : "\E[1;*D"
-key Left-Shift+Ansi-AppCursorKeys-AnyModifier : "\E[D"
-key Left-Shift+Ansi+AppCursorKeys-AnyModifier : "\EOD"
-key Left-Shift-Ansi : "\ED"
-key Left+Shift+AppScreen : "\E[1;*D"
-key Left-Shift+KeyPad+Ansi-AppCursorKeys : "\E[D"
-key Left-Shift+KeyPad+Ansi+AppCursorKeys : "\EOD"
-key M+Ctrl+AppScreen : "\E[4e"
-key PgDown-Shift+AnyModifier : "\E[6;*~"
-key PgDown-Shift-AnyModifier : "\E[6~"
-key PgDown+Shift-AppScreen : ScrollPageDown
-key PgDown-Shift+KeyPad : "\E[6~"
-key PgUp-Shift+AnyModifier : "\E[5;*~"
-key PgUp-Shift-AnyModifier : "\E[5~"
-key PgUp+Shift-AppScreen : ScrollPageUp
-key PgUp-Shift+KeyPad : "\E[5~"
-key Return+Shift : "\EOM"
-key Return-Shift-NewLine : "\r"
-key Return-Shift+NewLine : "\r\n"
-key Right-Shift+Ansi+AnyModifier : "\E[1;*C"
-key Right-Shift+Ansi-AppCursorKeys-AnyModifier : "\E[C"
-key Right-Shift+Ansi+AppCursorKeys-AnyModifier : "\EOC"
-key Right-Shift-Ansi : "\EC"
-key Right+Shift+AppScreen : "\E[1;*C"
-key Right-Shift+KeyPad+Ansi-AppCursorKeys : "\E[C"
-key Right-Shift+KeyPad+Ansi+AppCursorKeys : "\EOC"
-key Space+Ctrl : "\x00"
-key Tab+Ctrl+Ansi : "\E[27;5;9~"
-key Tab+Ctrl-Ansi : "\t"
-key Tab+Shift+Ansi : "\E[Z"
-key Tab+Shift-Ansi : "\t"
-key Tab-Shift : "\t"
-key Up-Shift+Ansi+AnyModifier : "\E[1;*A"
-key Up-Shift+Ansi-AppCursorKeys-AnyModifier : "\E[A"
-key Up-Shift+Ansi+AppCursorKeys-AnyModifier : "\EOA"
-key Up-Shift-Ansi : "\EA"
-key Up+Shift+AppScreen : "\E[1;*A"
-key Up+Shift-AppScreen : ScrollLineUp
-key Up-Shift+KeyPad+Ansi-AppCursorKeys : "\E[A"
-key Up-Shift+KeyPad+Ansi+AppCursorKeys : "\EOA"
+key F8+AnyModifier : "\E[19;*~"
index c6b33fae652b9b11dea22563dfd266b00f3aedfa..d293cd79b7eff8a10e32ae2b45ac80c79d27b338 100644 (file)
@@ -15,7 +15,9 @@ SemanticInputClick=true
 SemanticUpDown=false
 
 [Interaction Options]
+AllowMouseTracking=true
 AlternateScrolling=true
+CopyTextAsHTML=false
 MouseWheelZoomEnabled=true
 OpenLinksByDirectClickEnabled=false
 TextEditorCmd=6
@@ -24,7 +26,7 @@ UnderlineFilesEnabled=true
 WordCharacters=@-./_~?&=%+#:
 
 [Keyboard]
-KeyBindings=default
+KeyBindings=ian
 
 [Scrolling]
 HistorySize=100000