minor fixes and improvements
[distro-setup] / brc
diff --git a/brc b/brc
index e0f2a883d209344e04e4c5a7d4b4bc9cd46d0726..ff3b91fb84306bc7cee5ec924a20f37d34f8b314 100644 (file)
--- a/brc
+++ b/brc
@@ -238,8 +238,7 @@ export SL_INFO_DIR=/p/sshinfo
 if [[ -s $bashrc_dir/path-add-function ]]; then
   source $bashrc_dir/path-add-function
   if [[ $SSH_CLIENT ]]; then
-    # [[ -d /home/iank/.iank/e/e ]] mounts it unnecessarily, so use this.
-    if grep -qF /home/iank/.iank/e/e /etc/auto.iank /etc/exports &>/dev/null; then
+    if grep -qF /home/iank/.iank/e/e /etc/exports &>/dev/null; then
       export EMACSDIR=/home/iank/.iank/e/e
     fi
     path-add $bashrc_dir
@@ -260,11 +259,6 @@ if [[ $SOE ]]; then
   fi
 fi
 
-# based on readme.debian. dunno if this will break on other distros.
-if [[ -s /usr/share/wcd/wcd-include.sh ]]; then
-  source /usr/share/wcd/wcd-include.sh
-fi
-
 
 mysrc() {
   local path dir file
@@ -392,11 +386,6 @@ for num in {1..9}; do
 done
 
 
-b() {
-  # backwards
-  c -
-}
-
 hexipv4() {
   printf '%d.%d.%d.%d\n' $(echo $1 | sed 's/../0x& /g')
 }
@@ -426,19 +415,226 @@ utcl() { # utc 24 hour time to local hour 24 hour time
   echo "print( ($1  $(date +%z | sed -r 's/..$//;s/^(-?)0*/\1/')) % 24)"|python3
 }
 
-# c. better cd
-if type -p wcd &>/dev/null; then
-  if [[ $LC_INSIDE_EMACS ]]; then
-    c() { wcd -c -z 50 -o "$@"; }
-  else
-    # lets see what the fancy terminal does from time to time
-    c() { wcd -c -z 50 "$@"; }
+declare -a _iankdirf _iankdirb
+
+
+# ## old wcd, to be deleted
+# b() {
+#   # backwards
+#   c -
+# }
+# # shellcheck disable=SC2032
+# f() {
+#   # cd forward
+#   c +
+# }
+# cl() {
+#   # choose recent directory. cl = cd list
+#   c =
+# }
+# # c. better cd
+# if ! type -t c &>/dev/null; then
+#   if type -p wcd &>/dev/null; then
+#     if [[ $LC_INSIDE_EMACS ]]; then
+#       c() { wcd -c -z 50 -o "$@"; }
+#     else
+#       # lets see what the fancy terminal does from time to time
+#       c() { wcd -c -z 50 "$@"; }
+#     fi
+#   else
+#     c() { cd "$@"; }
+#   fi
+# fi
+
+# c. better cd.
+# keep 2 stacks, forward and back. the top of the back is $PWD
+# as long as the last directory change was due to c,b,or cl.
+c() {
+  # normally, the top of dirb is our current dir. if it isn't,
+  # put it on there, except we don't want to do that when we
+  # just launched a shell
+  if [[ $OLDPWD ]]; then
+    if (( ${#_iankdirb[@]} == 0 )) || [[ ${_iankdirb[-1]} != "$PWD" ]]; then
+      _iankdirb+=("$PWD")
+    fi
   fi
-else
-  c() { cd "$@"; }
-fi
+  cd "$@"
+  if (( ${#_iankdirb[@]} == 0 )) || [[ ${_iankdirb[-1]} != "$PWD" ]]; then
+    _iankdirb+=("$PWD")
+  fi
+  echo "$PWD" >> ~/.iankdirs
+}
 ccomp cd c
 
+bwm() {
+  s bwm-ng -T avg -d
+}
+
+b() {
+  local topb
+  if (( ${#_iankdirb[@]} == 0 )); then
+    echo "nothing left to go back to" >&2
+    return 0
+  fi
+  topb="${_iankdirb[-1]}"
+
+  if [[ $topb == "$PWD" ]] && (( ${#_iankdirb[@]} == 1 )); then
+    echo "already on last back entry" >&2
+    return 0
+  fi
+
+
+  if [[ $topb == "$PWD" ]]; then
+    # add to dirf if not already there
+    if (( ${#_iankdirf[@]} == 0 )) || [[ ${_iankdirf[-1]} != "$topb" ]]; then
+      _iankdirf+=("$topb")
+    fi
+    unset "_iankdirb[-1]"
+    cd "${_iankdirb[-1]}"
+  else
+    if (( ${#_iankdirf[@]} == 0 )) || [[ ${_iankdirf[-1]} != "$PWD" ]]; then
+      _iankdirf+=("$PWD")
+    fi
+    cd "$topb"
+  fi
+
+  # give us a peek at what is next in the list
+  # if (( ${#_iankdirb[@]} >= 2 )); then
+  #   printf "%s\n" "${_iankdirb[-2]}"
+  # fi
+}
+
+f() {
+  local topf
+  if (( ${#_iankdirf[@]} == 0 )); then
+    echo "no forward dir left" >&2
+    return 0
+  fi
+  topf="${_iankdirf[-1]}"
+  unset "_iankdirf[-1]"
+  c "$topf"
+
+  # give us a peek at what is next in the list
+  # if (( ${#_iankdirf[@]} )); then
+  #   printf "%s\n" "${_iankdirf[-1]}"
+  # fi
+}
+
+# a b c (d)
+## back
+# a b (c)
+# d
+#back
+#a (b)
+#d c
+#back
+#(a)
+#d c b
+#forward
+#a (b)
+#d c
+#
+# a b c
+## back
+# a b
+# (c)
+## forward
+
+cl() {
+  local i line input start tmpfile
+  local -A buttondirs alines
+  local -a buttons dirs lines
+  buttons=( {a..z} {2..9} )
+  if [[ ! -s ~/.iankdirs ]]; then
+    echo nothing in ~/.iankdirs
+    return 0
+  fi
+
+  i=0
+
+  # note, alternate approach like this, made the following read fail
+  # done < <(tac ~/.iankdirs | awk '!seen[$0]++')
+  # bash: read: read error: 0: Input/output error
+  # which went away by adding a sleep 1 after it.
+
+  mapfile -t lines <~/.iankdirs
+  start=$(( ${#lines[@]} - 1 ))
+
+  # we have ~33 buttons as of this writing, so lets
+  # prune down the history every once in a while.
+  if (( start > 500 )); then
+    tmpfile=$(mktemp)
+    tac ~/.iankdirs | awk '!seen[$0]++' | head -n 200 >$tmpfile
+    cat $tmpfile > ~/.iankdirs
+  fi
+
+  for (( j=$start; j >= 0; j-- )); do
+    line="${lines[$j]}"
+    if [[ ! $line || ${alines[$line]} || ! -d "$line" || $line == "$PWD" || line == "$HOME"  ]]; then
+      continue
+    fi
+    alines[$line]=t
+    buttondirs[${buttons[i]}]="$line"
+    printf "%s %s\n" ${buttons[i]} "$line"
+    if (( i == ${#buttons[@]} - 1 )); then
+      break
+    fi
+    i=$(( i + 1 ))
+  done
+
+  if (( i == 0 )); then
+    echo "no dirs in ~/.iankdirs"
+    return 0
+  fi
+  read -r -N 1 input
+  if [[ $input != $'\n' ]]; then
+    c ${buttondirs[$input]}
+  fi
+}
+bl() {
+  local start i j max
+  max=10
+  start=$(( ${#_iankdirb[@]} - 1 ))
+
+  # cleanup possible repeating of pwd
+  if (( start >= 0 )) && [[ ${_iankdirb[$start]} == "$PWD" ]]; then
+    start=$(( start - 1 ))
+  fi
+  j=1
+  if (( start >= 0 )); then
+    for (( i=$start; i >= 0 ; i-- )); do
+      printf "%s %s\n" $j ${_iankdirb[i]}
+      j=$(( j + 1 ))
+      if (( j >= max )); then
+        break
+      fi
+    done
+  fi
+
+  max=10
+
+  start=$(( ${#_iankdirf[@]} - 1 ))
+
+  # cleanup possible repeating of pwd
+  if (( start >= 0 )) && [[ ${_iankdirf[$start]} == "$PWD" ]]; then
+    start=$(( start - 1 ))
+  fi
+  if (( start < 0 )); then
+    return 0
+  fi
+  echo --
+  j=1
+  for (( i=$start; i >= 0 ; i-- )); do
+    printf "%s %s\n" $j ${_iankdirf[i]}
+    j=$(( j + 1 ))
+    if (( j >= max )); then
+      break
+    fi
+  done
+}
+
+
+
 c4() { c /var/log/exim4; }
 
 caa() { git commit --amend --no-edit -a; }
@@ -491,7 +687,7 @@ chrbind() {
 chumount() {
   local d
   # dev/pts needed for pacman signature check
-  for d in dev proc sys dev/pts; do
+  for d in dev/pts dev proc sys; do
     [[ -d $d ]]
     if mountpoint $d &>/dev/null; then
       m s umount $d
@@ -543,8 +739,8 @@ cdiff() {
 cat-new-files() {
   local start=$SECONDS
   local dir="$1"
-  inotifywait -m "$dir" -e create -e moved_to |
-    # shellcheck disable=SC2030
+  # shellcheck disable=SC2030
+  inotifywait -m "$dir" -e create -e moved_to | \
     while read -r filedir _ file; do
       cat "$filedir$file"
       hr
@@ -570,10 +766,6 @@ cim() {
   git commit -m "$*"
 }
 
-cl() {
-  # choose recent directory. cl = cd list
-  c =
-}
 
 d() { builtin bg "$@"; }
 ccomp bg d
@@ -591,6 +783,19 @@ despace() {
   done
 }
 
+# get ipv4 ip from HOST. or if it is already a number, return that
+hostip() {
+  local host="$1"
+  case $host in
+    [0-9:])
+      echo "$host"
+      ;;
+    *)
+      getent ahostsv4 "$host" | awk '{ print $1 }' | head -n1
+      ;;
+  esac
+}
+
 dig() {
   command dig +nostats +nocmd "$@"
 }
@@ -628,7 +833,10 @@ digdiff() {
 dt() {
   date "+%A, %B %d, %r" "$@"
 }
-ccomp date dt
+dtr() {
+  date -R "$@"
+}
+ccomp date dt dtr
 
 dus() { # du, sorted, default arg of
   du -sh ${@:-*} | sort -h
@@ -636,7 +844,7 @@ dus() { # du, sorted, default arg of
 ccomp du dus
 
 
-e() { echo "$@"; }
+e() { printf "%s\n" "$*"; }
 
 # echo args
 ea() {
@@ -670,9 +878,19 @@ ediff() {
 
 # mail related
 etail() {
+  ngset
+  tail -F /var/log/exim4/mainlog /var/log/exim4/*main -n 200 "$@"
+  ngreset
+}
+etailm() {
   tail -F /var/log/exim4/mainlog -n 200 "$@"
 }
-ccomp tail etail
+etail2() {
+  tail -F /var/log/exim4/mymain -n 200 "$@"
+}
+
+ccomp tail etail etail2
+
 
 # print exim old pids
 eoldpids() {
@@ -732,14 +950,18 @@ eless() {
 }
 ccomp less eless
 eqcat() {
-  exiqgrep -i -o 60 | while read -r i; do
+  exiqgrep -ir.\* -o 60 | while read -r i; do
     hlm exim -Mvc $i
     echo
     hlm exigrep $i /var/log/exim4/mainlog | cat ||:
   done
 }
 eqrmf() {
-  exiqgrep -i | xargs exim -Mrm
+  # other ways to get the list of message ids:
+  # exim -bp | awk 'NF == 4 {print $3}'
+  # # this is slower 160ms, vs 60.
+  # exipick -i
+  exiqgrep -ir.\* | xargs exim -Mrm
 }
 
 econfdevnew() {
@@ -756,11 +978,6 @@ econfdev() {
 
 
 
-# shellcheck disable=SC2032
-f() {
-  # cd forward
-  c +
-}
 
 fa() {
   # find array. make an array of file names found by find into $x
@@ -777,19 +994,15 @@ faf() { # find all files. use -L to follow symlinks
        -o -name .undo-tree-history -prune \) -type f 2>/dev/null
 }
 
-# todo: id like to do maybe a daily or hourly cronjob to
-# check that my history file size is increasing. Ive had it
-# inexplicably truncated in the past.
-histrm() {
-  history -n
-  history | awk -v IGNORECASE=1 '{ a=$1; sub(/^( *[^ ]+){4} */, "") }; /'"$*"'/'
-  read -p "press anything but contrl-c to delete"
-  for entry in $(history | awk -v IGNORECASE=1 '{ a=$1; sub(/^( *[^ ]+){4} */, "") }; /'"$*"'/ { print a }' | tac); do
-    history -d $entry
-  done
-  history -w
+# full path without resolving symlinks
+fp() {
+  local dir base
+  base="${1##*/}"
+  dir="${1%$base}"
+  printf "%s/%s\n" $(cd $dir; pwd) "$base"
 }
 
+
 # mail related
 frozen() {
   rm -rf /tmp/frozen
@@ -1029,6 +1242,7 @@ ipp() {
 
 ifn() {
   # insensitive find
+  # -L = follow symlinks
   find -L . -not \( -name .svn -prune -o -name .git -prune \
        -o -name .hg -prune -o -name .editor-backups -prune \
        -o -name .undo-tree-history -prune \) -iname "*$**" 2>/dev/null
@@ -1119,7 +1333,7 @@ low() {  # make filenames lowercase, remove bad chars
     fi
     f="${arg##*/}"
     new="${f,,}" # downcase
-    new="${new//[^[:alnum:]._-]/_}" # sub bad chars
+    new="${new//[^a-zA-Z0-9._-]/_}" # sub bad chars
     new="${new#"${new%%[[:alnum:]]*}"}" # remove leading/trailing non-alnum
     new="${new%"${new##*[[:alnum:]]}"}"
     # remove bad underscores, like __ and _._
@@ -1143,10 +1357,28 @@ lower() { # make first letter of filenames lowercase.
 k() { # history search
   grep -iP --binary-files=text "$@" ${HISTFILE:-~/.bash_history}  | tail -n 80 || [[ $? == 1 ]];
 }
-ks() { # history search
+ks() { # history search with context
+  # args are an extended regex used by sed
+  history | sed -nr "h;s/^\s*(\S+\s+){4}//;/$*/{g;p}" | tail -n 80 || [[ $? == 1 ]];
+}
+ksu() { # history search unique
   grep -P --binary-files=text "$@" ${HISTFILE:-~/.bash_history}  | uniq || [[ $? == 1 ]];
 }
-ccomp grep k ks
+
+# todo: id like to do maybe a daily or hourly cronjob to
+# check that my history file size is increasing. Ive had it
+# inexplicably truncated in the past.
+histrm() {
+  history -n
+  HISTTIMEFORMAT= history | awk -v IGNORECASE=1 '{ a=$1; sub(/^ *[^ ]+ */, "") }; /'"$*"'/'
+  read -p "press anything but contrl-c to delete"
+  for entry in $(HISTTIMEFORMAT= history | awk -v IGNORECASE=1 '{ a=$1; sub(/^ *[^ ]+ */, "") }; /'"$*"'/ { print a }' | tac); do
+    history -d $entry
+  done
+  history -w
+}
+
+ccomp grep k ks ksu histrm
 
 
 make-targets() {
@@ -1182,12 +1414,44 @@ nags() {
 }
 
 nmt() {
-  s nmtui-connect "$@"
+  # cant use s because sudo -i doesnt work for passwordless sudo command
+  case $EUID in
+    0)
+      sudo nmtui-connect "$@"
+      ;;
+    *)
+      nmtui-connect "$@"
+      ;;
+  esac
+}
+
+
+ngset() {
+  if shopt nullglob >/dev/null; then
+    ngreset=false
+  else
+    shopt -s nullglob
+    ngreset=true
+  fi
+}
+ngreset() {
+  if $ngreset; then
+    shopt -u nullglob
+  fi
 }
 
 nopanic() {
   # shellcheck disable=SC2024
-  sudo tee -a /var/log/exim4/paniclog-archive </var/log/exim4/paniclog; sudo truncate -s0 /var/log/exim4/paniclog
+  ngset
+  for f in /var/log/exim4/paniclog /var/log/exim4/*panic; do
+    base=${f##*/}
+    if [[ -s $f ]]; then
+      echo ================== $f =============
+      s tee -a /var/log/exim4/$base-archive <$f
+      s truncate -s0 $f
+    fi
+  done
+  ngreset
 }
 
 p8() { ping "$@" 8.8.8.8; }
@@ -1321,6 +1585,13 @@ rsu() { # [OPTS] HOST PATH
 }
 ccomp rsync rsd rsa rst rsu
 
+# find programs listening on a port
+ssp() {
+  local port=$1
+  # to figure out these args, i had to look at the man page from git version, as of 2022-04.
+  s ss -lpn state listening sport = $port
+}
+
 resolvcat() {
   local f
   if [[ $(systemctl is-active nscd ||:) != inactive ]]; then
@@ -1328,7 +1599,7 @@ resolvcat() {
   fi
   f=/etc/resolv.conf
   echo $f:;  ccat $f
-  hr; s ss -lpn 'sport = 53'
+  hr; s ss -lpn sport = 53
   if systemctl is-enabled dnsmasq &>/dev/null || [[ $(systemctl is-active dnsmasq ||:) != inactive ]]; then
     # this will fail is dnsmasq is failed
     hr; m ser status dnsmasq | cat || :
@@ -1343,7 +1614,7 @@ resolvcat() {
   grep '^ *hosts:' /etc/nsswitch.conf
   if systemctl is-enabled systemd-resolved &>/dev/null || [[ $(systemctl is-active systemd-resolved ||:) != inactive ]]; then
     hr; m ser status systemd-resolved | cat || :
-    hr; m systemd-resolve --status | cat
+    hr; m resolvectl status | cat
   fi
 
 }
@@ -1372,6 +1643,10 @@ rmstrips() {
   ssh fencepost head -n 300 /gd/gnuorg/EventAndTravelInfo/rms-current-trips.txt | less
 }
 
+sudo () {
+  command sudo "$@" || return $?
+  DID_SUDO=true
+}
 s() {
   # background
   # I use a function because otherwise we cant use in a script,
@@ -1489,23 +1764,15 @@ sgu() {
 sk() {
 
 
-  # note, if you do something like this
-  # x=( prefix* )
-  # then disable the warning with:
-  # shellcheck disable=SC2206 # globbing is intended
-
-  # 2029: "unescaped, this expands on the client side.": yes, I know how ssh works
-  # 2164: "Use 'cd ... || exit' or 'cd ... || return' in case cd fails.": i have automatic error handling
-  # 2086: unquoted $var: Quoting every var I set is way too much quotes.
-  # 2068: Double quote array expansions to avoid re-splitting elements: same as above.
-  # 2033: command arg is a function name: too many false positives.
-
+  # disable a warning with:
+  # shellcheck disable=SC2206 # reasoning
 
-  # these ones I had disabled, but without a good written explanation, so enabling them temporarily
-  # 2046: unquoted $(cmd)
-  # 2119: Functions with optional args get bad warnings when none are passed.
+  # see bash-template/style-guide.md for justifications
 
-  shellcheck -W 999 -x -e 2029,2164,2086,2068,2033 "$@" || return $?
+  local quotes others
+  quotes=2048,2068,2086,2206
+  others=2029,2033,2054,2164
+  shellcheck -W 999 -x -e $quotes,$others "$@" || return $?
 }
 
 
@@ -1894,6 +2161,12 @@ psnetns() {
       if [[ $x ]]; then echo "$x"; else echo $l; fi;
     done
 }
+nonet() {
+  if ! s ip netns list | grep -Fx nonet &>/dev/null; then
+    s ip netns add nonet
+  fi
+  sudo -E env /sbin/ip netns exec nonet sudo -E -u iank /bin/bash
+}
 
 m() { printf "%s\n" "$*";  "$@"; }
 
@@ -1941,6 +2214,85 @@ s/^\Wcapability: (.*)/\1/;Ta;h;b
 "|sort -r
 }
 
+# Run script by copying it to a temporary location first,
+# and changing directory, so we don't have any open
+# directories or files that could cause problems when
+# remounting.
+z() {
+  local tmp
+  tmp=$(type -p "$1")
+  if [[ $tmp ]]; then
+    cd $(mktemp -d)
+    cp -a "$tmp" .
+    shift
+    ./"${tmp##*/}" "$@"
+  else
+    "$@"
+  fi
+}
+
+
+# * spark
+# spark 1 5 22 13 53
+#   # => ▁▁▃▂▇
+
+# The MIT License
+# Copyright (c) Zach Holman, https://zachholman.com
+# https://github.com/holman/spark
+
+# As of 2022-10-28, I reviewed github forks that had several newer
+# commits, none had anything interesting. I did a little refactoring
+# mostly to fix emacs indent bug.
+
+# Generates sparklines.
+_spark_echo()
+{
+  if [ "X$1" = "X-n" ]; then
+    shift
+    printf "%s" "$*"
+  else
+    printf "%s\n" "$*"
+  fi
+}
+
+
+spark()
+{
+  local f tc
+  local n numbers=
+
+  # find min/max values
+  local min=0xffffffff max=0
+
+  for n in ${@//,/ }
+  do
+    # on Linux (or with bash4) we could use `printf %.0f $n` here to
+    # round the number but that doesn't work on OS X (bash3) nor does
+    # `awk '{printf "%.0f",$1}' <<< $n` work, so just cut it off
+    n=${n%.*}
+    (( n < min )) && min=$n
+    (( n > max )) && max=$n
+    numbers=$numbers${numbers:+ }$n
+  done
+
+  # print ticks
+  local ticks=(▁ ▂ ▃ ▄ ▅ ▆ ▇ █)
+
+  # use a high tick if data is constant
+  (( min == max )) && ticks=(▅ ▆)
+
+  tc=${#ticks[@]}
+  f=$(( ( (max-min) <<8)/( tc - 1) ))
+  (( f < 1 )) && f=1
+
+  for n in $numbers
+  do
+    _spark_echo -n ${ticks[$(( ((($n-$min)<<8)/$f) ))]}
+  done
+  _spark_echo
+}
+
+
 # * misc stuff