summaryrefslogtreecommitdiff
path: root/sh
diff options
context:
space:
mode:
authorXiao Pan <gky44px1999@gmail.com>2024-02-23 03:08:52 -0800
committerXiao Pan <gky44px1999@gmail.com>2024-02-23 03:08:52 -0800
commitdd78faaf40862bd1552ec36c41374b527f747284 (patch)
treeb914f890c545db7168984f7bcc15cfc7c976c1d3 /sh
parentc808bb6ee7f77f077308a1f06f63dbee6a928f6d (diff)
Reorganize shell scripts into sh dir, consider in the future fsh will have different kinds of files
Diffstat (limited to 'sh')
-rwxr-xr-xsh/alarm11
-rwxr-xr-xsh/backlight23
-rwxr-xr-xsh/bell7
-rwxr-xr-xsh/ccgp7
-rwxr-xr-xsh/cfg29
-rwxr-xr-xsh/chmodef6
-rwxr-xr-xsh/curlqb27
-rwxr-xr-xsh/dateft29
-rwxr-xr-xsh/dirnameall19
-rwxr-xr-xsh/gita17
-rwxr-xr-xsh/gitfork10
-rwxr-xr-xsh/gitmetap17
-rwxr-xr-xsh/gitmetar7
-rwxr-xr-xsh/gitpu19
-rwxr-xr-xsh/grrc21
-rwxr-xr-xsh/il6
-rwxr-xr-xsh/lastarg5
-rwxr-xr-xsh/loop15
-rwxr-xr-xsh/lsp3
-rwxr-xr-xsh/mll6
-rwxr-xr-xsh/mmi27
-rwxr-xr-xsh/mpra44
-rwxr-xr-xsh/mpva17
-rwxr-xr-xsh/mpvy45
-rwxr-xr-xsh/mvln27
-rwxr-xr-xsh/mvtr11
-rwxr-xr-xsh/mvtu13
-rwxr-xr-xsh/news39
-rwxr-xr-xsh/o27
-rwxr-xr-xsh/orgext18
-rwxr-xr-xsh/pa8
-rwxr-xr-xsh/pq13
-rwxr-xr-xsh/px10
-rwxr-xr-xsh/qg15
-rwxr-xr-xsh/qw16
-rwxr-xr-xsh/rate11
-rwxr-xr-xsh/reco28
-rwxr-xr-xsh/rfp11
-rwxr-xr-xsh/sbar79
-rwxr-xr-xsh/ta5
-rwxr-xr-xsh/time-uuid6
-rwxr-xr-xsh/topa8
-rwxr-xr-xsh/upd219
-rwxr-xr-xsh/vinfo4
-rwxr-xr-xsh/wh11
-rwxr-xr-xsh/wtr43
-rwxr-xr-xsh/xmq3
47 files changed, 1042 insertions, 0 deletions
diff --git a/sh/alarm b/sh/alarm
new file mode 100755
index 0000000..5342e7d
--- /dev/null
+++ b/sh/alarm
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+min="$1"
+[ "$1" ] && shift
+printf "notify-send -u critical %s; bell" "$(/usr/bin/printf '%q ' "${@:-alarm}")" | at now + "${min:-0}" minutes && [ "$min" ] && [ "$min" -ne 0 ] && notify-send "Successfully Scheduled"
+
+# at used to not like string like 'Boiling Water!', don't know why now it works, if see similar issue, use folllowing codes
+#at now + "$1" minutes << EOF && notify-send 'Successfully Scheduled'
+#notify-send -u critical "${2:-alarm}"
+#bell
+#EOF
diff --git a/sh/backlight b/sh/backlight
new file mode 100755
index 0000000..3de87f5
--- /dev/null
+++ b/sh/backlight
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+brightness=$(cat /sys/class/backlight/intel_backlight/brightness)
+max_brightness=$(cat /sys/class/backlight/intel_backlight/max_brightness)
+step=$((max_brightness/20))
+
+case "$1" in
+ '-u')
+ case "$brightness" in
+ 0) echo 1;;
+ 1) echo "$step";;
+ *) echo "$((brightness+step))";;
+ esac
+ ;;
+ '-d')
+ case "$brightness" in
+ 1) echo 0;;
+ "$step") echo 1;;
+ *) echo "$((brightness-step))";;
+ esac
+ ;;
+ *) exit 1;;
+esac > /sys/class/backlight/intel_backlight/brightness
diff --git a/sh/bell b/sh/bell
new file mode 100755
index 0000000..a0395ac
--- /dev/null
+++ b/sh/bell
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+bef="$(amixer get Master | awk -F '[][]' 'END{print $2}')"
+amixer set Master 10%
+# code steal from https://unix.stackexchange.com/a/163716/459013
+speaker-test -t sine -f 1000 -l 1
+amixer set Master "$bef"
diff --git a/sh/ccgp b/sh/ccgp
new file mode 100755
index 0000000..2f5754f
--- /dev/null
+++ b/sh/ccgp
@@ -0,0 +1,7 @@
+#!/bin/sh
+# steal codes from https://github.com/LukeSmithxyz/voidrice/blob/master/.local/bin/compiler
+
+base="${1%.*}"
+# -lm link math library? needed for sqrt(). not sure
+# https://stackoverflow.com/questions/44175151/what-is-the-meaning-of-lm-in-gcc
+cc -Wall -lgsl -lgslcblas -lm -lplplot -o "$base" "$1" && ./"$base"
diff --git a/sh/cfg b/sh/cfg
new file mode 100755
index 0000000..92a097e
--- /dev/null
+++ b/sh/cfg
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+umask 077
+while getopts lsmM opt; do
+ case $opt in
+ #c)
+ # work_tree_dir="$HOME"
+ # meta_file="$XDG_CONFIG_HOME/myconf/cfg${opt}_meta"
+ # cmd="git --git-dir=$HOME/.cfg$opt/ --work-tree=$HOME"
+ # ;;
+ l|s)
+ work_tree_dir=/
+ meta_file="/etc/myconf/cfg${opt}_meta"
+ cmd="sudo -E git --git-dir=/etc/.cfg$opt/ --work-tree=/"
+ ;;
+ m)
+ sudo gitmetap "$cmd" "$work_tree_dir" "$meta_file"
+ $cmd diff
+ exit
+ ;;
+ M)
+ sudo gitmetar "$meta_file"
+ exit
+ ;;
+ \?) exit 1;;
+ esac
+done
+shift $((OPTIND-1))
+$cmd "$@"
diff --git a/sh/chmodef b/sh/chmodef
new file mode 100755
index 0000000..446bc23
--- /dev/null
+++ b/sh/chmodef
@@ -0,0 +1,6 @@
+#!/bin/sh
+# chmodef: CHMOD DEFault
+
+# https://wiki.archlinux.org/title/File_permissions_and_attributes#Numeric_method
+find "$@" -type d -execdir chmod 755 -- '{}' \+
+find "$@" -type f -execdir chmod 644 -- '{}' \+
diff --git a/sh/curlqb b/sh/curlqb
new file mode 100755
index 0000000..d2851cd
--- /dev/null
+++ b/sh/curlqb
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# need --no-clobber if don't want overwrite when same name
+# if do this also need change .py.1 to .py else qbt will not accept
+# also if a.py and a_1.py, qbt will not accept the latter, maybe need to change some .py codes inside
+# now, I choose to let three files to be overwrited, those are "with categories" .py files that overwrite without categories .py files, which is what I want so I don't use --no-clobber
+curl -s 'https://github.com/qbittorrent/search-plugins/wiki/Unofficial-search-plugins' | awk -F'"' '
+/user-content-plugins-for-private-sites/ {exit}
+/\.py/ {
+ url=$2
+ getline
+ getline
+ getline
+ if($0 !~ /❗|✖/)
+ print url
+}
+' | xargs curl -s -Z -L --remote-name-all --create-dirs --output-dir "$1" --
+
+# an alternative awk command:
+#awk -F'"' '
+#/require an account/ {exit}
+#/\.py/ {
+# url=$2
+# line=NR
+#}
+#url && NR==line+3 && !/❗|✖/ {print url}
+#'
diff --git a/sh/dateft b/sh/dateft
new file mode 100755
index 0000000..b317474
--- /dev/null
+++ b/sh/dateft
@@ -0,0 +1,29 @@
+#!/bin/sh
+# DATE From To
+
+while getopts cfF:t:T: opt; do
+ case $opt in
+ c) tz_from="Asia/Shanghai";;
+ f) tz_from="$(tzselect)";;
+ F) tz_from="$OPTARG";;
+ # one extra ',' at the end will not let `for tz in ...` loop one extra time?
+ t) for i in $(seq "$OPTARG"); do
+ tz_to="$(tzselect),$tz_to"
+ done;;
+ T) tz_to="$OPTARG,$tz_to";;
+ \?) exit 1;;
+ esac
+done
+shift $((OPTIND-1))
+
+time_from="TZ=\"${tz_from:-$(readlink /etc/localtime | sed 's#.*/\(.*/.*\)$#\1#')}\" ${1:-now}"
+
+date -d "$time_from"
+TZ="Asia/Shanghai" date -d "$time_from"
+date -d "$time_from" -u
+date -d "$time_from" +%s
+[ "$tz_from" ] && [ "$tz_from" != "Asia/Shanghai" ] && TZ="$tz_from" date -d "$time_from"
+IFS=','
+for tz in $tz_to; do
+ TZ="$tz" date -d "$time_from"
+done
diff --git a/sh/dirnameall b/sh/dirnameall
new file mode 100755
index 0000000..befd347
--- /dev/null
+++ b/sh/dirnameall
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+while read -r line; do
+ if [ "$paths" ]; then
+ paths="$paths
+$line"
+ else
+ paths="$line"
+ fi
+done
+
+dirs="$paths"
+while [ "$dirs" != '.' ]; do
+ [ "$dirs" ] && paths="$paths
+$dirs"
+ dirs="$(echo "$dirs" | tr '\n' '\0' | xargs -0 dirname | awk '!a[$0]++')"
+done
+
+echo "$paths" | awk '!a[$0]++' | grep -v '^\.$'
diff --git a/sh/gita b/sh/gita
new file mode 100755
index 0000000..ee00306
--- /dev/null
+++ b/sh/gita
@@ -0,0 +1,17 @@
+#!/bin/sh
+# note message string must come after options
+
+cmd=git
+
+while getopts ls f; do
+ case $f in
+ l|s) cmd="cfg -$f";;
+ \?) exit 1;;
+ esac
+done
+shift $((OPTIND-1))
+
+mes="${1:-update}"
+
+$cmd commit --all -m "$mes"
+$cmd push
diff --git a/sh/gitfork b/sh/gitfork
new file mode 100755
index 0000000..9736c4c
--- /dev/null
+++ b/sh/gitfork
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+git clone "$1"
+cd "$(basename "$1" .git)" || exit
+git remote add upstream "$2"
+if ! git checkout fly; then
+ git branch fly
+ git checkout fly
+ git push --set-upstream origin fly
+fi
diff --git a/sh/gitmetap b/sh/gitmetap
new file mode 100755
index 0000000..c058986
--- /dev/null
+++ b/sh/gitmetap
@@ -0,0 +1,17 @@
+#!/bin/sh
+# GIT META Print
+
+# -rw-r--r-- == binary 110100100 == octal 644
+# https://stackoverflow.com/a/1796009
+$1 ls-tree -r --name-only --full-tree HEAD | dirnameall | awk -v a="$2/" '{printf("%s%s\n",a,$0)}' | tr '\n' '\0' | xargs -0 ls -ldA -- | awk '{
+ k=0
+ for(i=0;i<=8;i++)
+ k+=((substr($1,i+2,1)~/[rwx]/)*2^(8-i))
+ if(k) {
+ for(i=10;$i && $i!="->";i++)
+ $9=$9" "$i
+ printf("%0o %s %s %s\n",k,$3,$4,$9)
+ }
+}' > "$3"
+
+#$1 ls-tree -r --name-only --full-tree HEAD | awk -v a="$2/" '{printf("%s%s\n",a,$0)}' | tr '\n' '\0' | xargs -0 ls -ldA -- | awk '{k=0;for(i=0;i<=8;i++)k+=((substr($1,i+2,1)~/[rwx]/)*2^(8-i));if(k){for(i=10;$i && $i!="->";i++)$9=$9" "$i;printf("%0o %s %s %s\n",k,$3,$4,$9)}}' > "$3"
diff --git a/sh/gitmetar b/sh/gitmetar
new file mode 100755
index 0000000..1bfdc15
--- /dev/null
+++ b/sh/gitmetar
@@ -0,0 +1,7 @@
+#!/bin/sh
+# GIT META Read
+
+while read -r mod user group path; do
+ chown -hc "$user:$group" -- "$path"
+ [ "$mod" -ne 777 ] && chmod -c "$mod" -- "$path"
+done < "$1"
diff --git a/sh/gitpu b/sh/gitpu
new file mode 100755
index 0000000..5453fae
--- /dev/null
+++ b/sh/gitpu
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+if [ -z "$1" ]; then
+ git checkout master
+ git pull upstream master
+ git push
+ git checkout fly
+ git merge --no-edit master
+ git push
+else
+ for dir; do
+ git -C "$dir" checkout master
+ git -C "$dir" pull upstream master
+ git -C "$dir" push
+ git -C "$dir" checkout fly
+ git -C "$dir" merge --no-edit master
+ git -C "$dir" push
+ done
+fi
diff --git a/sh/grrc b/sh/grrc
new file mode 100755
index 0000000..adf665e
--- /dev/null
+++ b/sh/grrc
@@ -0,0 +1,21 @@
+#!/bin/sh
+# GRep -R Code
+
+# logic: no descend, in this dir, if a dir name .git; then true and do nothing; else if a file name LICENSE; then true and do nothing; else print
+[ "$1" ] && find "$HOME/programs/fsh" \
+ "$HOME/archive/programs/public_archive_codes" \
+ "$HOME/archive/programs/private_archive_codes" \
+ "$HOME/archive/programs/me106_proj_fall2021" \
+ "$HOME/archive/programs/car_job/av_dev" \
+ "$HOME/archive/programs/VisualDimension" \
+ "$XDG_DOCUMENTS_DIR/sjsu_courses/spring_2024/math161a/hw" \
+ "$XDG_DOCUMENTS_DIR/notes" \
+ -mindepth 1 -maxdepth 1 -type d -name '.git' -o -type f -name 'LICENSE' -o -print0 2>/dev/null \
+ | xargs -0 grep -s --color=always -iIR "$@" \
+ "$HOME/.local/bin" \
+ "$HOME/.bashrc" \
+ "$HOME/.profile" \
+ "$HOME/.xinitrc" \
+ "$HOME/programs/suckless/dwm_fly/config.def.h" \
+ "$XDG_CONFIG_HOME/nsxiv/exec/key-handler" \
+ | "$PAGER"
diff --git a/sh/il b/sh/il
new file mode 100755
index 0000000..7c75a71
--- /dev/null
+++ b/sh/il
@@ -0,0 +1,6 @@
+#!/bin/sh
+# InterpoLation
+
+# x=$1, xa=$2, xb=$3, ya=$4, yb=$5
+# https://en.wikipedia.org/wiki/Interpolation#Linear_interpolation
+echo "scale=4;$4+($5-$4)*($1-$2)/($3-$2)" | bc
diff --git a/sh/lastarg b/sh/lastarg
new file mode 100755
index 0000000..b52afcd
--- /dev/null
+++ b/sh/lastarg
@@ -0,0 +1,5 @@
+#!/bin/sh
+# usage: lastarg "$@"
+
+shift $(($# - 1))
+echo "$1"
diff --git a/sh/loop b/sh/loop
new file mode 100755
index 0000000..6a57826
--- /dev/null
+++ b/sh/loop
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+while getopts t: opt; do
+ case $opt in
+ t) time="$OPTARG";;
+ \?) exit 1;;
+ esac
+done
+shift $((OPTIND-1))
+
+while :; do
+ # suggested by https://github.com/koalaman/shellcheck/wiki/SC2294
+ "$@"
+ sleep "${time:-1}"
+done
diff --git a/sh/lsp b/sh/lsp
new file mode 100755
index 0000000..4e2caa0
--- /dev/null
+++ b/sh/lsp
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+loop ssh "${1:-pp}"
diff --git a/sh/mll b/sh/mll
new file mode 100755
index 0000000..9f7d5df
--- /dev/null
+++ b/sh/mll
@@ -0,0 +1,6 @@
+#!/bin/sh
+# Map Latitude Longitude
+
+# https://help.openstreetmap.org/questions/9669/jumping-to-a-specific-set-of-coordinates-on-the-map
+# another way is to manually search xx.xxxxx,xx.xxxxx
+o "$BROWSER" "https://www.openstreetmap.org/?mlat=$1&mlon=$2&zoom=5"
diff --git a/sh/mmi b/sh/mmi
new file mode 100755
index 0000000..350fe50
--- /dev/null
+++ b/sh/mmi
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+mi () {
+ make
+ sudo make install
+}
+
+origin="$PWD"
+cmd='mi'
+
+while getopts q opt; do
+ case $opt in
+ q) cmd='qmake6;mi';;
+ \?) exit 1;;
+ esac
+done
+shift $((OPTIND-1))
+
+if [ -z "$1" ]; then
+ eval "$cmd"
+else
+ for dir; do
+ cd "$dir" || exit
+ eval "$cmd"
+ cd "$origin" || exit
+ done
+fi
diff --git a/sh/mpra b/sh/mpra
new file mode 100755
index 0000000..0801e55
--- /dev/null
+++ b/sh/mpra
@@ -0,0 +1,44 @@
+#!/bin/sh
+# MakePkg Repo-Add
+
+fn () {
+ if ! { [ -f PKGBUILD ] || [ -h PKGBUILD ];}; then
+ exit
+ fi
+ find . -maxdepth 1 -type f -name "*.pkg.tar.$ext*" -delete
+ makepkg --sign -sf
+ newpkg="$(find . -maxdepth 1 -type f -name "*.pkg.tar.$ext")"
+ [ -d ../repo ] || mkdir ../repo
+ mv "$newpkg" "$newpkg.sig" ../repo
+ # need manually repo-add new database for first time initialization
+ repo-add -s -v -R "$(find ../repo -maxdepth 1 -type f -name '*.db.tar.gz')" "../repo/$newpkg"
+ # seems no need to delete *.old and *.old.sig, see https://mirror.fcix.net/archlinux/core/os/x86_64/ it do include *.old
+ #find ../repo -maxdepth 1 -type f \( -name '*.old' -o -name '*.old.sig' \) -delete
+}
+
+ext=zst
+copy_any=
+
+while getopts ac opt; do
+ case $opt in
+ # arch linux arm still using .xz instead of .zst as package compression format
+ a) ext=xz;;
+ # copy arch linux fly-any repo
+ c) copy_any=1;;
+ \?) exit 1;;
+ esac
+done
+shift $((OPTIND-1))
+
+if [ $# -eq 0 ]; then
+ fn
+else
+ for dir; do
+ cd -- "$(realpath -- "$dir")" || exit
+ fn
+ done
+fi
+
+[ "$copy_any" ] && sudo rsync -vPrlt --delete ../repo/ /srv/http/mirrors/archlinux/fly/os/any
+
+upd -p
diff --git a/sh/mpva b/sh/mpva
new file mode 100755
index 0000000..d80c9f3
--- /dev/null
+++ b/sh/mpva
@@ -0,0 +1,17 @@
+#!/bin/sh
+# MPV Audio
+
+# fzf has --read0, maybe useful
+# use --loop-file for single file, better performance maybe
+# fzf --scheme=path for better fzf result when searching path, like the old time:
+# https://github.com/junegunn/fzf/commit/6fb41a202a97ad3f2437f6e5aee8890268560412
+
+if [ $# -eq 0 ]; then
+ find "$XDG_MUSIC_DIR" | fzf -m --print0 --scheme=path
+fi | xargs -0 sh -c '
+if [ $# -eq 1 ] && ! [ -d "$1" ]; then
+ mpv --af= --loop-file=inf --video=no "$1"
+else
+ mpv --af= --shuffle --loop-playlist=inf --video=no "$@"
+fi
+' shell "$@"
diff --git a/sh/mpvy b/sh/mpvy
new file mode 100755
index 0000000..eb935cc
--- /dev/null
+++ b/sh/mpvy
@@ -0,0 +1,45 @@
+#!/bin/sh
+# MPV Yank/Youtube videos
+# references:
+# https://www.rockyourcode.com/til-how-to-watch-youtube-videos-with-mpv-and-keyboard-shortcuts/
+
+fps=30
+height=$SCR_HEIGHT
+url="$(xsel -ob)"
+flag=s
+
+# option f and h may do nothing if redownload? since same filename exist. yt-dlp won't download same file even without --auto-file-renameing=false. how improve? pass argument to aria2c?
+while getopts Aabd:f:h:su: opt; do
+ case $opt in
+ # s: streaming, a: aria2c then mpv, A: aria2c
+ A|a|s) flag=$opt;;
+ b) format='bestvideo+bestaudio/best';;
+ d) download_dir="$OPTARG";;
+ f) fps="$OPTARG";;
+ h) height="$OPTARG";;
+ u) url="$OPTARG";;
+ \?) exit 1;;
+ esac
+done
+if [ -z "$format" ]; then
+ # xyzstudio GPU
+ #vformat="[height<=?$height][fps<=?$fps][vcodec!^=?vp9][vcodec!^=?av01]"
+ vformat="[height<=?$height][fps<=?$fps][vcodec!^=?av01]"
+ # usually get .mp4 video + .webm audio on youtube, yt-dlp needs to merge them to .mkv
+ # it used to cause problem, but I forget
+ format="bestvideo$vformat+bestaudio/best$vformat"
+fi
+
+case $flag in
+ # here if use --write-sub, mpv doesn't recognize subtitles?
+ # --embed-subs is a little bit better, but still worse then direct streaming
+ # --sponsorblock-remove will make audio/video goes out of sync, need --force-keyframes-at-cuts which need re-encode which is slow, more see comments at the bottom of https://github.com/yt-dlp/yt-dlp/issues/871
+ # another way is using mpv-sponsorblock-minimal-git for streaming and sponsorblock-mpv-local for local videos
+ A) yt-dlp -f "$format" --embed-subs -P "${download_dir:-"$XDG_DOWNLOAD_DIR/mpvy/"}" --sponsorblock-remove default --sponsorblock-mark default "$url";;
+ a) yt-dlp -f "$format" --embed-subs -P "${download_dir:-"$XDG_DOWNLOAD_DIR/mpvy/"}" --sponsorblock-remove default --sponsorblock-mark default "$url" --exec 'mpv --fs --speed=2';;
+ s) mpv --ytdl-format="$format" --ytdl-raw-options='write-sub=' --fs --speed=2 "$url";;
+esac
+# not sure if this is the best practice, but it seems working ;)
+status=$?
+[ $status -ne 0 ] && notify-send 'mpvy failed'
+exit $status
diff --git a/sh/mvln b/sh/mvln
new file mode 100755
index 0000000..54cef54
--- /dev/null
+++ b/sh/mvln
@@ -0,0 +1,27 @@
+#!/bin/sh
+# edge cases give me headache
+
+lns () {
+ # prevent `mvln file1 file1` or `mvln dir1/file1 dir1/`
+ # which means `ln -s samename samename` or `ln -s dir/file dir/`
+ # both $1 and $2 here are realpath
+ [ "$1" != "$2" ] && ln -s -- "$1" "$2"
+}
+
+mv -i -- "$@"
+# consider `mvln file1 dir/file2`
+if [ $# -eq 2 ] && ! [ -d "$2" ]; then
+ # use realpath here
+ lns "$(realpath "$2")" "$(realpath "$1")"
+else
+ dir="$(realpath -- "$(lastarg "$@")")"
+ # steal from https://unix.stackexchange.com/a/353833/459013
+ while [ $# -gt 1 ]; do
+ to="$(realpath -- "$1")"
+ # prevent `mvln dir1/ dir1/`
+ if [ "$dir" != "$to" ]; then
+ lns "$dir/$(basename "$1")" "$to"
+ fi
+ shift
+ done
+fi
diff --git a/sh/mvtr b/sh/mvtr
new file mode 100755
index 0000000..28eebf0
--- /dev/null
+++ b/sh/mvtr
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# can use ls pipe to sed pipe to bash, similar to eval? see comment at link:
+# link also show perl-rename approach
+# https://linuxconfig.org/rename-all-files-from-uppercase-to-lowercase-characters
+# if only change ' ' to '_' `rename` might be sufficient, for upper to lower case might need perl-rename if want concise
+
+# ./_filename will be renamed ./filename, not what I want, need improve
+# -exec can't replace -execdir here, can write a -exec version
+
+find "$@" -depth -execdir sh -c 'dest="$(echo "$1" | tr -d "\047" | sed -E -e "s/([[:lower:]])([[:upper:]])/\1_\2/g" | tr "[:upper:] " "[:lower:]_" | tr -s "[:punct:]" | sed -E -e "s#/-#/#g" -e "s/_([[:punct:]])/\1/g" -e "s/([[:punct:]])_/\1/g" | tr -s "[:punct:]")"; [ -e "$dest" ] || mv -v -- "$1" "$dest"' shell '{}' \;
diff --git a/sh/mvtu b/sh/mvtu
new file mode 100755
index 0000000..6160507
--- /dev/null
+++ b/sh/mvtu
@@ -0,0 +1,13 @@
+#!/bin/sh
+# MV Time-Uuid
+
+find "$@" -type f -execdir sh -c '
+ file="$(basename "$1")"
+ ext="${file##*.}"
+ if [ "$ext" != "$file" ]; then
+ dest="$(time-uuid).$ext"
+ else
+ dest="$(time-uuid)"
+ fi
+ [ -e "dest" ] || mv -v -- "$1" "$dest"
+' shell '{}' \;
diff --git a/sh/news b/sh/news
new file mode 100755
index 0000000..4d4b5c1
--- /dev/null
+++ b/sh/news
@@ -0,0 +1,39 @@
+#!/bin/sh
+
+daily () {
+ # mail.yahoo.com/d/folders/6 is spam folder
+ o "$BROWSER" \
+ https://github.com/notifications \
+ 'https://mail.google.com/mail/u/0/#inbox' \
+ 'https://mail.google.com/mail/u/0/#spam' \
+ 'https://mail.google.com/mail/u/1/#inbox' \
+ 'https://mail.google.com/mail/u/1/#spam' \
+ https://mail.yahoo.com \
+ https://mail.yahoo.com/d/folders/6 \
+ https://en.wikipedia.org/wiki/Portal:Current_events
+ # alacritty --hold must be before -e
+ # full window: -w156, am and pm in one line: -w114
+ o alacritty --hold -e rem -cu+2 -@ -w114
+ o alacritty --hold -e wtr
+ o alacritty -e newsboat
+ o alacritty --hold -e rate
+ o alacritty --hold -e pass practice_password2
+}
+
+monthly () {
+ o "$BROWSER" https://smtp.cheogram.com/ http://download.huzheng.org
+ # Show 3 months' remind. I use 3 months because it is better to know I20 deadline 3 months earlier so I have 2 months to prepare the money for extension.
+ # (2*31+30+6)/7=14. Consider month 1 day 1 is on Sunday and make sure month 3 day 31 is shown. Tested with `rem -cu+14 -@ -w114 2025-03-01`
+ o alacritty --hold -e sh -c 'rem -cu+14 -@ -w114 | $PAGER'
+}
+
+if [ $# -eq 0 ]; then
+ daily
+else
+ while getopts m opt; do
+ case $opt in
+ m) monthly;;
+ \?) exit 1;;
+ esac
+ done
+fi
diff --git a/sh/o b/sh/o
new file mode 100755
index 0000000..8fb066c
--- /dev/null
+++ b/sh/o
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# useful links
+# https://stackoverflow.com/questions/3430330/best-way-to-make-a-shell-script-daemon
+# https://serverfault.com/questions/117152/do-background-processes-get-a-sighup-when-logging-off
+# https://wiki.archlinux.org/title/default_applications
+
+# I don't care about errors, I don't want to handle errors, just shut up please.
+# nohup seems not necessary if don't care about daemon SIGHUP requirement
+
+# when close a shell script's stdout or stderr with `>&-` or `2>&-`, `xsel -ib` in that script has no effect
+# https://github.com/kfish/xsel/issues/43
+# using `>&-` or `2>&-` doesn't completely close mpv's output when run sth. like `o mpv file.mkv`
+# base on above observations, I choose to use `>/dev/null 2>&1` instead
+
+# need ; before } when it is in the same line as { ? https://www.shellcheck.net/wiki/SC1056
+
+if [ $# -eq 1 ] && [ -e "$1" ] && { ! [ -x "$1" ] || [ -d "$1" ];}; then
+ case "$1" in
+ # libreoffice, please don't let firefox eat your shit
+ *.docx|*.pptx) cmd=libreoffice;;
+ *) cmd=xdg-open;;
+ esac
+ nohup "$cmd" "$1" 0<&- >/dev/null 2>&1 &
+else
+ nohup "$@" 0<&- >/dev/null 2>&1 &
+fi
diff --git a/sh/orgext b/sh/orgext
new file mode 100755
index 0000000..0143e62
--- /dev/null
+++ b/sh/orgext
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+outdir () {
+ find . -type f -execdir mv -t "$PWD" -- '{}' \+
+ find . -mindepth 1 -maxdepth 1 -type d -execdir rmdir -- '{}' \+
+}
+
+indir () {
+ # https://stackoverflow.com/questions/1842254/how-can-i-find-all-of-the-distinct-file-extensions-in-a-folder-hierarchy
+ find . -maxdepth 1 -type f | awk -F. '!a[$NF]++{print $NF}' | xargs mkdir --
+ find . -mindepth 1 -maxdepth 1 -type d -execdir basename -az -- '{}' \+ | xargs -0 -I _ find . -maxdepth 1 -type f -name '*._' -execdir mv -t '_' -- '{}' \+
+}
+
+case "$1" in
+ '-o') outdir;;
+ '-i') indir;;
+ *) outdir; indir;;
+esac
diff --git a/sh/pa b/sh/pa
new file mode 100755
index 0000000..86ae4e5
--- /dev/null
+++ b/sh/pa
@@ -0,0 +1,8 @@
+#!/bin/sh
+# Ping Alarm
+
+while ! ping -w5 "$1"; do
+ sleep 5
+done
+notify-send "$1 on"
+bell
diff --git a/sh/pq b/sh/pq
new file mode 100755
index 0000000..bad9c35
--- /dev/null
+++ b/sh/pq
@@ -0,0 +1,13 @@
+#!/bin/sh
+# Pacman Query, to manually determine remove the package or not
+
+[ "$1" ] && {
+ #sed -n '/^Depends On/,/^Conflicts With/{/^Conflicts With/!p}; /^Description\|^URL\|^Groups\|^Installed Size\|^Install Reason/p'
+ pacman -Qi "$1" | awk '/^Groups/{f=1} /^Conflicts With/{f=0} f||/^Description|^URL|^Installed Size|^Install Reason/'
+ printf '\n'
+ pacman -Ql "$1" | grep '/bin/[^$]'
+ printf '\n'
+ pactree -r "$1"
+ printf '\n'
+ grrc "$1"
+} | "$PAGER"
diff --git a/sh/px b/sh/px
new file mode 100755
index 0000000..1b4e849
--- /dev/null
+++ b/sh/px
@@ -0,0 +1,10 @@
+#!/bin/sh
+# Printf Xsel
+
+if [ $# -eq 0 ]; then
+ /usr/bin/printf '%q' "$PWD"
+else
+ for dir; do
+ /usr/bin/printf '%q ' "$(realpath -- "$dir")"
+ done
+fi | xsel -ib
diff --git a/sh/qg b/sh/qg
new file mode 100755
index 0000000..11f6ff6
--- /dev/null
+++ b/sh/qg
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+# more resources see comments in my script qw
+# https://guix.gnu.org/manual/en/html_node/Running-Guix-in-a-VM.html
+# `-enable-kvm` same as `-accel kvm`, see archwiki
+qemu-system-x86_64 \
+ -accel kvm \
+ -cpu host \
+ -device virtio-blk,drive=myhd \
+ -display gtk,zoom-to-fit=on \
+ -drive if=none,file="$XDG_DOWNLOAD_DIR/qemu/guix.img",format=qcow2,id=myhd \
+ -m 8G \
+ -nic user,model=virtio-net-pci,hostfwd=::53684-:22 \
+ -smp 2,sockets=1,cores=1,threads=2 \
+ -usb -device usb-tablet
diff --git a/sh/qw b/sh/qw
new file mode 100755
index 0000000..e77b88f
--- /dev/null
+++ b/sh/qw
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+# For better performance, it needs CPU-pinning with `taskset`, GPU pass-through, and other more tweaks
+# https://github.com/cardi/qemu-windows-10
+# https://wiki.archlinux.org/title/QEMU
+## https://wiki.archlinux.org/title/QEMU#Mouse_integration
+# https://qemu-project.gitlab.io/qemu/system/devices/usb.html
+qemu-system-x86_64 \
+ -accel kvm \
+ -cpu host,hv_relaxed,hv_spinlocks=0x1fff,hv_vapic,hv_time \
+ -display gtk,zoom-to-fit=on \
+ -drive file="$XDG_DOWNLOAD_DIR/qemu/win10.img",format=qcow2 \
+ -m 8G \
+ -nic user,hostfwd=::53683-:22 \
+ -smp 2,sockets=1,cores=1,threads=2 \
+ -usb -device usb-tablet
diff --git a/sh/rate b/sh/rate
new file mode 100755
index 0000000..1625df6
--- /dev/null
+++ b/sh/rate
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+while getopts f:t: opt; do
+ case $opt in
+ f) from="$OPTARG";;
+ t) to="$(echo "$OPTARG" | tr '[:lower:]' '[:upper:]')";;
+ \?) exit 1;;
+ esac
+done
+# https://docs.cloud.coinbase.com/sign-in-with-coinbase/docs/api-exchange-rates
+curl -s "https://api.coinbase.com/v2/exchange-rates?currency=${from:-xmr}" | jq -r ".data.rates.${to:-USD}"
diff --git a/sh/reco b/sh/reco
new file mode 100755
index 0000000..7ee726d
--- /dev/null
+++ b/sh/reco
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# useful urls:
+# https://github.com/LukeSmithxyz/voidrice/blob/master/.local/bin/dmenurecord
+# https://wiki.archlinux.org/title/FFmpeg#Screen_capture
+# https://trac.ffmpeg.org/wiki/Capture/Desktop
+
+infofile="/tmp/recoinfo"
+recodir="$XDG_VIDEOS_DIR/recordings/"
+tmpdir="$recodir/tmp/"
+mkdir -p "$recodir" "$tmpdir"
+
+if [ -e "$infofile" ]; then
+ read -r pid tmpfile < "$infofile"
+ kill "$pid"
+ notify-send 'finish recording, start converting'
+ ffmpeg -i "$tmpfile" "$recodir/$(time-uuid).mkv"
+ notify-send 'finish converting'
+ rm "$tmpfile" "$infofile"
+else
+ tmpfile="$tmpdir/$(time-uuid).mkv"
+ notify-send 'prepare recording'
+ # arch wiki way, no audio, less cpu use during capturing (fast?), large file size, need convert afterward
+ xrectsel '%w %h %x %y' | xargs sh -c 'ffmpeg -y -loglevel quiet -f x11grab -framerate 25 -s "$2x$3" -i "$DISPLAY+$4,$5" -c:v ffvhuff "$1" & echo $!' shell "$tmpfile" | xargs -I {} printf '%s\t%s' '{}' "$tmpfile" > "$infofile"
+ # another way to get subshell ffmpeg child pid
+ #xrectsel '%w %h %x %y' | xargs sh -c 'ffmpeg -y -f x11grab -framerate 25 -s "$2x$3" -i "$DISPLAY+$4,$5" -c:v ffvhuff "$1"' shell "$tmpfile" &
+ #printf '%s\t%s' "$(ps -o pid= --ppid $!)" "$tmpfile" > "$infofile"
+fi
diff --git a/sh/rfp b/sh/rfp
new file mode 100755
index 0000000..c1a3e1f
--- /dev/null
+++ b/sh/rfp
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+if grep -q '^//user_pref("privacy.resistFingerprinting", false);$' "$HOME/.mozilla/firefox/xxxxxxxx.fly/user-overrides.js"; then
+ sed -i 's#^//\(user_pref("privacy.resistFingerprinting", false);\)$#\1#' "$HOME/.mozilla/firefox/xxxxxxxx.fly/user-overrides.js"
+ notify-send -u critical 'RFP disabled'
+else
+ sed -i 's#^\(user_pref("privacy.resistFingerprinting", false);\)$#//\1#' "$HOME/.mozilla/firefox/xxxxxxxx.fly/user-overrides.js"
+ notify-send 'RFP enabled'
+fi
+upd -j
+o "$BROWSER"
diff --git a/sh/sbar b/sh/sbar
new file mode 100755
index 0000000..18c62f1
--- /dev/null
+++ b/sh/sbar
@@ -0,0 +1,79 @@
+#!/bin/sh
+# modified from pystardust, GPLv3 license: https://github.com/pystardust/sbar
+
+# INIT
+sec=0
+
+# MODULES
+update_time () {
+ time="$(date '+%a %m/%d %H:%M') $(TZ=Asia/Shanghai date '+/%d %H:') $(date -u '+/%d %H:')"
+}
+
+#update_cap () {
+# cap="$(if xset q | grep -q "Caps Lock: *on"; then echo A; else echo a; fi)"
+#}
+
+update_net () {
+ net="$(if nmcli -t --fields type,state device | grep -q '^\(ethernet\|wifi\):connected$'; then echo 1; else echo 0; fi)"
+ nm_device_state="$(nmcli -t --fields device,state device)"
+ vpn="$(
+ if echo "$nm_device_state" | grep -q '^wg_ka:connected$'; then
+ echo K
+ elif echo "$nm_device_state" | grep -q '^wg_studio:connected$'; then
+ echo S
+ else
+ echo 0
+ fi
+ )"
+}
+
+update_vol () {
+ # $(NF-1) for both alsa and pulseaudio
+ vol="$(amixer get Master | awk -F'[][]' 'END{printf("%d %s",($(NF-1)=="on")?1:0,$2)}')"
+}
+
+update_mic () {
+ # $(NF-1) for both alsa and pulseaudio
+ mic="$(amixer get Capture | awk -F '[][]' 'END{print ($(NF-1)=="on")?1:0}')"
+}
+
+update_bat () {
+ bat="$(cat /sys/class/power_supply/BAT0/capacity)%"
+}
+
+update_gpu () {
+ gpu="$(envycontrol -q | awk 'END{print toupper(substr($NF,0,1))}')"
+}
+
+display () {
+ xsetroot -name "$time | N $net V $vpn | M $vol C $mic | $gpu | $bat"
+}
+
+# modules that don't update on their own need to be run at the start for getting their initial value
+update_net
+update_vol
+update_mic
+update_gpu
+
+# SIGNALLING
+# trap "<function>;display" "RTMIN+n"
+trap "update_mic;display" "RTMIN"
+trap "update_vol;display" "RTMIN+1"
+# xev can't read my toggle internet keyboard key, don't know what key to use in sxhkd to send signal
+trap "update_net;display" "RTMIN+2"
+# to update it from external commands
+## kill -m $(pidof -x sbar)
+# where m = 34 + n
+
+while :; do
+ sleep 1 &
+ wait
+ [ $((sec % 5 )) -eq 0 ] && update_time # update time every 5 seconds
+ [ $((sec % 60)) -eq 0 ] && update_net
+ [ $((sec % 60)) -eq 0 ] && update_bat
+ # zoom turn on my mic on start, so need to monitor the change
+ [ $((sec % 60 )) -eq 0 ] && update_mic
+ # how often the display updates ( 5 seconds )
+ [ $((sec % 5 )) -eq 0 ] && display
+ sec=$((sec + 1))
+done
diff --git a/sh/ta b/sh/ta
new file mode 100755
index 0000000..ae386c1
--- /dev/null
+++ b/sh/ta
@@ -0,0 +1,5 @@
+#!/bin/sh
+# TIme Alarm
+
+time -p "$@"
+o alarm 0 'Command Finished' "$(/usr/bin/printf '%q ' "$@")"
diff --git a/sh/time-uuid b/sh/time-uuid
new file mode 100755
index 0000000..bdaf4b3
--- /dev/null
+++ b/sh/time-uuid
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+# echo current nanosecond since epoch and alpha-numerically ordered UUID
+# https://stackoverflow.com/questions/28681650/generate-alpha-numerically-ordered-uuids-over-time?noredirect=1&lq=1
+# https://askubuntu.com/questions/342842/what-does-this-command-mean-awk-f-print-4
+echo "$(date '+%s-%N')-$(uuidgen -t | awk -F- '{OFS="-"; print $3,$2,$1,$4,$5}')"
diff --git a/sh/topa b/sh/topa
new file mode 100755
index 0000000..3252997
--- /dev/null
+++ b/sh/topa
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+o alacritty -e htop
+#o alacritty -e radeontop -c
+o alacritty -e iotop
+o alacritty -e nethogs
+o alacritty -e intel_gpu_top
+o alacritty -e nvtop
diff --git a/sh/upd b/sh/upd
new file mode 100755
index 0000000..12499da
--- /dev/null
+++ b/sh/upd
@@ -0,0 +1,219 @@
+#!/bin/sh
+
+all () {
+ fast
+ clean
+ [ "$hostname" != xyzpp ] && refl
+ [ "$hostname" = xyzka ] && qb
+ [ "$hostname" = xyzinsp ] && music
+ if [ "$hostname" = xyzinsp ] || [ "$hostname" = xyzpp ]; then
+ userjs
+ fi
+}
+
+backup () {
+ case "$hostname" in
+ xyzinsp) backup_branch=master;;
+ *) backup_branch=${hostname#xyz};;
+ esac
+
+ if [ "$hostname" = xyzinsp ]; then
+ # Pull repos and branches from studio, so one more backup on insp or duplicity will backup to ka,for one more backup for 321 backup rule
+ # cfgl master and pp branches already satisfy 321 rule. Here are three copies: 1. master/pp, 2. studio, 3. gitlab or alternative
+ # But I still copy those on insp because one more copy will not hurt. And all my other data stil satisfy 3 copies without backup to gitlab, so why left out cfgl? Also it may be nice if I what to use that local dir for searching things.
+ cd "$HOME/programs/config_local_arch" || exit 1
+ git branch | awk '{print ($1=="*")?$2:$1}' | while read -r branch; do
+ git checkout "$branch"
+ git pull
+ # Push ka branch to codeberg or any future alternative website for mirroring. Needed for public codes/data, considering my death may result in all copies on my computers being lost.
+ [ "$branch" = ka ] && git push
+ done
+ # git checkout will change mtime, need to change back so rsync won't think it needs to backup these files
+ # https://stackoverflow.com/q/1964470/9008720
+ # https://stackoverflow.com/q/21735435/9008720
+ # https://github.com/MestreLion/git-tools
+ # aur/git-tools-git
+ git restore-mtime
+
+ (
+ umask 077
+ # backup ka cfgl
+ sudo -E git -C /root/programs/config_local_arch_secrets_ka pull
+ # backup studio cfgl
+ sudo -E git -C /root/programs/config_local_arch_secrets pull
+ )
+
+ # rsync backup from studio to insp
+ rsync -avPR --delete studio:/home/xyz/.config/qBittorrent :/home/xyz/.local/share/qBittorrent/BT_backup "$HOME/backup/studio"
+
+ fi
+
+ if [ "$hostname" = xyzinsp ] || [ "$hostname" = xyzstudio ]; then
+ # rsync backup from ka
+ rsync -avPR --delete ka:/home/xyz/.config/qBittorrent :/home/xyz/.local/share/qBittorrent/BT_backup "$HOME/backup/ka"
+ fi
+
+ if [ "$hostname" != xyzstudio ] && [ "$hostname" != xyzka ]; then
+ # rsync backup to studio
+ # --files-from make -a not imply -r, so need to specify explicitly
+ rsync -avPRr --delete --files-from="$XDG_CONFIG_HOME/myconf/upd_rsync_files" / "studio:/home/xyz/backup/$backup_branch"
+ fi
+
+ if [ "$hostname" != xyzka ]; then
+ # duplicity backup to ka
+ # https://wiki.archlinux.org/title/Duplicity
+ # Need // for absolute path, see manpage URL FORMAT section. If not use //, will store to /home/xyz/home/xyz/...
+ # --files-from has a bug, this bug makes source url that is / not working while /home works, more see vq notes
+ # --use-agent not working when ssh to pp and insp, works on insp, not sure why
+ use_agent=
+ [ "$hostname" = xyzinsp ] && use_agent='--use-agent'
+ sudo duplicity --ssh-askpass $use_agent --encrypt-key 9790577D2BE328D46838117ED3F54FE03F3C68D6 --sign-key 05899270DF25BB1EEDF57BE824F769E5D08C9E9A --full-if-older-than 2Y --include /etc/.cfgs --include-filelist "/home/xyz/.config/myconf/upd_rsync_files" --exclude / / "sftp://xyz@xyzka.kyun.li//home/xyz/backup/$backup_branch"
+ fi
+}
+
+clean () {
+ if [ "$hostname" = xyzinsp ]; then
+ nsxiv -c
+ # my ways
+ # -exec can't replace -execdir here
+ find "$XDG_CACHE_HOME/nsxiv/" -depth -type d -empty -execdir rmdir -- '{}' \+
+ # -exec can replace -execdir here
+ #find "$XDG_CACHE_HOME/nsxiv/" -depth -type d -execdir rmdir --ignore-fail-on-non-empty -- '{}' \+
+ # nsxiv man page way
+ #find "$XDG_CACHE_HOME/nsxiv/" -depth -type d -empty ! -name '.' -exec rmdir -- '{}' \;
+
+ fi
+
+ if [ "$hostname" = xyzinsp ] || [ "$hostname" = xyzpp ]; then
+ cd "$HOME/.mozilla/firefox/xxxxxxxx.fly/prefsjs_backups" || exit
+ # https://stackoverflow.com/a/34862475/9008720
+ ls -t | tail -n +11 | tr '\n' '\0' | xargs -0 rm --
+ # https://unix.stackexchange.com/questions/92095/reset-atq-list-to-zero
+ sudo systemctl stop atd
+ echo 0 | sudo tee /var/spool/atd/.SEQ > /dev/null
+ sudo systemctl start atd
+ rm -rf "$XDG_VIDEOS_DIR/recordings/tmp/"
+ fi
+
+ paru -aSc --noconfirm
+}
+
+# basic daily stuff
+fast () {
+ pac
+ misc
+ [ "$hostname" != xyzka ] && backup
+}
+
+userjs () {
+ kill $(pidof "$BROWSER")
+ # change working dir for cleaner
+ cd "$HOME/.mozilla/firefox/xxxxxxxx.fly" || exit
+ arkenfox-cleaner -s
+ arkenfox-updater -s
+}
+
+misc () {
+ "$EDITOR" +PlugClean! +PlugUpdate +qa
+
+ if [ "$hostname" = xyzinsp ] || [ "$hostname" = xyzpp ]; then
+ tldr --update
+ fi
+
+ if [ "$hostname" = xyzinsp ]; then
+ sudo hardcode-fixer
+ ssh pp '[ -s "$HOME/.local/share/sdcv_history" ] && cat "$HOME/.local/share/sdcv_history" && rm "$HOME/.local/share/sdcv_history"' >> "$XDG_DATA_HOME/sdcv_history"
+ awk '!a[$0]++' "$XDG_DATA_HOME/sdcv_history" | sponge "$XDG_DATA_HOME/sdcv_history"
+ # temperory solution before find a way of using git submodule or subtree with `cfg -l`
+ git -C "$HOME/.mozilla/firefox/xxxxxxxx.fly/chrome/firefox-csshacks" pull
+ git -C "$XDG_DOCUMENTS_DIR/notes" commit --all -m 'update'
+ git -C "$XDG_DOCUMENTS_DIR/notes" push
+ git -C "$HOME/programs/reminders" commit --all -m 'update'
+ git -C "$HOME/programs/reminders" push
+ pass git push
+ fi
+
+ if [ "$hostname" = xyzpp ]; then
+ git -C "$XDG_DOCUMENTS_DIR/notes" pull
+ git -C "$HOME/programs/reminders" pull
+ pass git pull
+ fi
+}
+
+pac () {
+ pacpacs="$(sudo pacman --noconfirm -Syu | tee /dev/tty | grep -m1 '^Packages' | cut -d' ' -f3-)"
+ # Update rust toolchains before paru so paru can compile things in newest rust if needed.
+ rustup update
+ aurpacs="$(paru --color never --noconfirm -aSu --ignore libredwg-git | tee /dev/tty | grep '^Aur' | cut -d' ' -f3-)"
+ # part steal from aur comment
+ # sometimes "ERROR: Failure while downloading": https://github.com/neovim/neovim/issues/15709
+ # echo 1, printf 1 and yes 1 all works? not sure why
+ # aur neovim-nightly-bin has some issue on 12/26/2021? switch to community repo neovim temporary
+ #rm -rf ~/.cache/paru/clone/neovim-nightly-bin/ && echo 1 | PARU_PAGER=cat paru --rebuild --redownload neovim-nightly-bin
+ pacman -Qqme > "$XDG_CONFIG_HOME/myconf/pacman_Qqme"
+ pacman -Qqne > "$XDG_CONFIG_HOME/myconf/pacman_Qqne"
+ systemctl list-unit-files --state=enabled > "$XDG_CONFIG_HOME/myconf/sye"
+ systemctl --user list-unit-files --state=enabled > "$XDG_CONFIG_HOME/myconf/syue"
+ # pacdiff default use pacman database, so no need `sudo -E` for find, but will be a little bit slower
+ log="$log
+updated pacman packages: $pacpacs
+updated aur packages: $aurpacs
+pacdiff: $(pacdiff -o | tr '\n' ' ')
+checkrebuild: $(checkrebuild | awk '$2!~"zoom|miniconda3"{printf("%s ",$2)}')
+$(if echo "$pacpacs" | grep -q 'linux-\(megi-\)\?[0-9]'; then echo 'kernel upgraded, need reboot'; fi)
+"
+}
+
+qb () {
+ sudo systemctl stop qbittorrent-nox@xyz.service
+ find "$XDG_DATA_HOME/qBittorrent/nova3/engines" -maxdepth 1 -type f ! -name 'jackett*' -a ! -name '__init__.py' -delete
+ curlqb "$XDG_DATA_HOME/qBittorrent/nova3/engines"
+ sudo systemctl start qbittorrent-nox@xyz.service
+}
+
+refl () {
+ # why not use http:
+ # https://www.reddit.com/r/archlinux/comments/kx149z/should_i_use_http_mirrors/
+ # https://www.reddit.com/r/archlinux/comments/ej4k4d/is_it_safe_to_use_not_secured_http_pacman_mirrors/
+ # rsync may need to change XferCommand in /etc/pacman.conf
+ # https://www.reddit.com/r/archlinux/comments/mynw6e/rsync_mirrors_with_pacman/
+ # need --delay so no super out of sync mirrors
+ if [ "$hostname" != xyzka ]; then
+ sudo reflector --verbose --save /etc/pacman.d/mirrorlist --country us --protocol https --delay 1 --latest 25 --score 25 --fastest 10
+ else
+ sudo reflector --verbose --save /etc/pacman.d/mirrorlist --country ro --protocol https --delay 1 --fastest 3
+ fi
+}
+
+music () {
+ yt-dlp -f 'bestaudio[ext=opus]/bestaudio' --match-filter 'license=cc-by' --match-filter 'title~=(?i)cc-by' -P "$XDG_MUSIC_DIR/cc-by/scott_buckley" https://soundcloud.com/scottbuckley/tracks
+ rsync -avP --delete "$XDG_MUSIC_DIR/cc-by/scott_buckley" pp:/home/xyz/music/cc-by
+ rsync -avP --delete "$XDG_MUSIC_DIR/favorite" pp:/home/xyz/music
+}
+
+hostname="$(hostname)"
+[ "$hostname" = xyzpp ] && gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type nothing
+if [ $# -eq 0 ]; then
+ fast
+else
+ while getopts abcfjmMpqr opt; do
+ case $opt in
+ a)all;;
+ b)backup;;
+ c)clean;;
+ f)fast;;
+ j)userjs;;
+ m)misc;;
+ M)music;;
+ p)pac;;
+ q)qb;;
+ r)refl;;
+ \?)exit 1;;
+ esac
+ done
+fi
+[ "$log" ] && printf '%s' "$log" | tee "$XDG_DOCUMENTS_DIR/logs/upd.log"
+[ "$hostname" = xyzpp ] && gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type suspend
+# `[ "$hostname" = xyzpp ] && ...` if check failed will have exit status of 1, unlike check with `if`
+# I decided to always `exit 0` if reached end, so commands like `upd -p && ...` can keep running
+exit 0
diff --git a/sh/vinfo b/sh/vinfo
new file mode 100755
index 0000000..190de19
--- /dev/null
+++ b/sh/vinfo
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+# steal from https://github.com/HiPhish/info.vim
+"$EDITOR" -RM +"Info $1 $2" +only
diff --git a/sh/wh b/sh/wh
new file mode 100755
index 0000000..d20f0de
--- /dev/null
+++ b/sh/wh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# blue, see `man terminfo`
+setaf="$(tput setaf 4)"
+sgr0="$(tput sgr0)"
+
+# dash `command -V "$@"` will only output first command's type, same as `type`, but different than posix specification? so not use here
+for cmd in type whatis whereis which "pacman -Qo --color always"; do
+ printf '\n%s\n%s\n' "$setaf$cmd $*$sgr0" "$($cmd "$@" 2>&1)"
+ #printf '\n\033[0;34m%s\033[0m\n%s\n' "$setaf$cmd $*$sgr0" "$($cmd "$@" 2>&1)"
+done | "$PAGER"
diff --git a/sh/wtr b/sh/wtr
new file mode 100755
index 0000000..c9907d9
--- /dev/null
+++ b/sh/wtr
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+# https://github.com/open-meteo/open-meteo
+i=0
+curl -s -G -d "latitude=${1:-37.34}" -d "longitude=${2:--121.89}" -d 'daily=weathercode,temperature_2m_max,temperature_2m_min,sunrise,sunset' -d 'timezone=auto' 'https://api.open-meteo.com/v1/forecast' | jq -r '[.daily|.time, .weathercode, .temperature_2m_max, .temperature_2m_min, .sunrise, .sunset]|transpose|.[]|@tsv' | while read -r time code max min sunrise sunset; do
+ # https://open-meteo.com/en/docs
+ case "$code" in
+ 0) code='clear sky';;
+ 1) code='mainly clear';;
+ 2) code='partly cloudy';;
+ 3) code=overcast;;
+ 45) code=fog;;
+ 48) code='depositing rime fog';;
+ 51) code='light drizzle';;
+ 53) code='moderate drizzle';;
+ 55) code='dense drizzle';;
+ 56) code='light freezing drizzle';;
+ 57) code='dense freezing drizzle';;
+ 61) code='slight rain';;
+ 63) code='moderate rain';;
+ 65) code='heavy rain';;
+ 66) code='light freeze rain';;
+ 67) code='heavy freeze rain';;
+ 71) code='slight snow fall';;
+ 73) code='moderate snow fall';;
+ 75) code='heavy snow fall';;
+ 77) code='snow grains';;
+ 80) code='slight rain showers';;
+ 81) code='moderate rain showers';;
+ 82) code='violent rain showers';;
+ 85) code='slight snow showers';;
+ 86) code='heavy snow showers';;
+ 95) code='slight or moderate thunderstorm';;
+ 96) code='thunderstorm with slight hail';;
+ 99) code='thunderstorm with heavy hail';;
+ *) exit 1;;
+ esac
+ if [ "$i" -ne 1 ]; then
+ printf 'sunrise: %s | sunset: %s\n' "${sunrise#*T}" "${sunset#*T}"
+ i=1
+ fi
+ printf '%s\t%s\t%s\t%s\n' "${time#*-}" "$max" "$min" "$code"
+done
diff --git a/sh/xmq b/sh/xmq
new file mode 100755
index 0000000..83eca0f
--- /dev/null
+++ b/sh/xmq
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+xdg-mime query filetype "$1" | tee /dev/tty | xargs xdg-mime query default