From 9871b292885e717364b6beee4cf69f8ac86938f3 Mon Sep 17 00:00:00 2001 From: Konstantin Demin Date: Tue, 9 Sep 2025 20:53:55 +0300 Subject: [PATCH] treewide: move zsh functions to sh scripts --- .config/dotfiles/gitignore | 52 ++- .config/dotfiles/install.sh | 9 +- .config/dotfiles/scripts/.keep | 0 .config/dotfiles/scripts/_perl-wrapper.sh | 22 + .config/dotfiles/scripts/_rsync-wrapper.sh | 45 ++ .config/dotfiles/scripts/apt-add-keyring | 430 ++++++++++++++++++ .config/dotfiles/scripts/apt-env | 11 + .config/dotfiles/scripts/apt-http-fetch | 51 +++ .config/dotfiles/scripts/apt-install | 9 + .config/dotfiles/scripts/apt-remove | 14 + .config/dotfiles/scripts/apt-repo-get-fpr | 80 ++++ .config/dotfiles/scripts/apt-search | 52 +++ .config/dotfiles/scripts/apt-update | 53 +++ .config/dotfiles/scripts/check-bin-pkg | 44 ++ .config/dotfiles/scripts/crlf-fix | 4 + .config/dotfiles/scripts/deb-src-export | 254 +++++++++++ .config/dotfiles/scripts/deb-ver-parse | 23 + .config/dotfiles/scripts/dperl | 1 + .config/dotfiles/scripts/dpkg-list-auto | 49 ++ .config/dotfiles/scripts/dpkg-list-installed | 6 + .config/dotfiles/scripts/dpkg-list-manual | 15 + .config/dotfiles/scripts/dpkg-list-martians | 24 + .config/dotfiles/scripts/dpkg-search | 25 + .config/dotfiles/scripts/dpkg-source-raw | 280 ++++++++++++ .config/dotfiles/scripts/dpkg-which | 91 ++++ .../dotfiles/scripts/ensure-eof-empty-line | 35 ++ .config/dotfiles/scripts/from | 1 + .config/dotfiles/scripts/gpg-batch | 59 +++ .config/dotfiles/scripts/gpg-export | 29 ++ .config/dotfiles/scripts/gpg-sign-file | 38 ++ .config/dotfiles/scripts/gpg-warmup | 10 + .config/dotfiles/scripts/idle | 20 + .config/dotfiles/scripts/krd-debsrc | 13 + .config/dotfiles/scripts/krd-quilt | 60 +++ .config/dotfiles/scripts/krd-sbuild | 115 +++++ .../dotfiles/scripts/openssl-cert-auto-pem | 117 +++++ .../scripts/openssl-generate-dh-bundle | 28 ++ .config/dotfiles/scripts/openssl-ocsp | 209 +++++++++ .config/dotfiles/scripts/pip-env | 11 + .config/dotfiles/scripts/quiet | 38 ++ .config/dotfiles/scripts/quilt-series-auto | 10 + .../scripts/quilt-series-strip-comments | 4 + .config/dotfiles/scripts/run-as | 148 ++++++ .config/dotfiles/scripts/sperl | 1 + .config/dotfiles/scripts/static-compress | 89 ++++ .config/dotfiles/scripts/to | 1 + .config/dotfiles/scripts/try-read-stdin | 42 ++ .config/dotfiles/scripts/ufind.sh | 8 + .config/dotfiles/scripts/zap-tree | 30 ++ .config/zsh/alias/gpg.zsh | 14 - .config/zsh/alias/{idle.zsh => idle.zsh.old} | 0 .config/zsh/alias/quilt.zsh | 84 ---- .config/zsh/alias/sbuild.zsh | 84 ---- .config/zsh/env.zsh | 2 +- 54 files changed, 2753 insertions(+), 191 deletions(-) delete mode 100644 .config/dotfiles/scripts/.keep create mode 100755 .config/dotfiles/scripts/_perl-wrapper.sh create mode 100755 .config/dotfiles/scripts/_rsync-wrapper.sh create mode 100755 .config/dotfiles/scripts/apt-add-keyring create mode 100755 .config/dotfiles/scripts/apt-env create mode 100755 .config/dotfiles/scripts/apt-http-fetch create mode 100755 .config/dotfiles/scripts/apt-install create mode 100755 .config/dotfiles/scripts/apt-remove create mode 100755 .config/dotfiles/scripts/apt-repo-get-fpr create mode 100755 .config/dotfiles/scripts/apt-search create mode 100755 .config/dotfiles/scripts/apt-update create mode 100755 .config/dotfiles/scripts/check-bin-pkg create mode 100755 .config/dotfiles/scripts/crlf-fix create mode 100755 .config/dotfiles/scripts/deb-src-export create mode 100755 .config/dotfiles/scripts/deb-ver-parse create mode 120000 .config/dotfiles/scripts/dperl create mode 100755 .config/dotfiles/scripts/dpkg-list-auto create mode 100755 .config/dotfiles/scripts/dpkg-list-installed create mode 100755 .config/dotfiles/scripts/dpkg-list-manual create mode 100755 .config/dotfiles/scripts/dpkg-list-martians create mode 100755 .config/dotfiles/scripts/dpkg-search create mode 100755 .config/dotfiles/scripts/dpkg-source-raw create mode 100755 .config/dotfiles/scripts/dpkg-which create mode 100755 .config/dotfiles/scripts/ensure-eof-empty-line create mode 120000 .config/dotfiles/scripts/from create mode 100755 .config/dotfiles/scripts/gpg-batch create mode 100755 .config/dotfiles/scripts/gpg-export create mode 100755 .config/dotfiles/scripts/gpg-sign-file create mode 100755 .config/dotfiles/scripts/gpg-warmup create mode 100755 .config/dotfiles/scripts/idle create mode 100755 .config/dotfiles/scripts/krd-debsrc create mode 100755 .config/dotfiles/scripts/krd-quilt create mode 100755 .config/dotfiles/scripts/krd-sbuild create mode 100755 .config/dotfiles/scripts/openssl-cert-auto-pem create mode 100755 .config/dotfiles/scripts/openssl-generate-dh-bundle create mode 100755 .config/dotfiles/scripts/openssl-ocsp create mode 100755 .config/dotfiles/scripts/pip-env create mode 100755 .config/dotfiles/scripts/quiet create mode 100755 .config/dotfiles/scripts/quilt-series-auto create mode 100755 .config/dotfiles/scripts/quilt-series-strip-comments create mode 100755 .config/dotfiles/scripts/run-as create mode 120000 .config/dotfiles/scripts/sperl create mode 100755 .config/dotfiles/scripts/static-compress create mode 120000 .config/dotfiles/scripts/to create mode 100755 .config/dotfiles/scripts/try-read-stdin create mode 100755 .config/dotfiles/scripts/ufind.sh create mode 100644 .config/dotfiles/scripts/zap-tree delete mode 100644 .config/zsh/alias/gpg.zsh rename .config/zsh/alias/{idle.zsh => idle.zsh.old} (100%) delete mode 100644 .config/zsh/alias/quilt.zsh delete mode 100644 .config/zsh/alias/sbuild.zsh diff --git a/.config/dotfiles/gitignore b/.config/dotfiles/gitignore index 7ec9f09..d24b2c6 100644 --- a/.config/dotfiles/gitignore +++ b/.config/dotfiles/gitignore @@ -9,7 +9,52 @@ !/.config/dotfiles/gitignore !/.config/dotfiles/gitignore.vcs !/.config/dotfiles/install.sh -!/.config/dotfiles/scripts/.keep +!/.config/dotfiles/scripts/_perl-wrapper.sh +!/.config/dotfiles/scripts/_rsync-wrapper.sh +!/.config/dotfiles/scripts/apt-add-keyring +!/.config/dotfiles/scripts/apt-env +!/.config/dotfiles/scripts/apt-http-fetch +!/.config/dotfiles/scripts/apt-install +!/.config/dotfiles/scripts/apt-remove +!/.config/dotfiles/scripts/apt-repo-get-fpr +!/.config/dotfiles/scripts/apt-search +!/.config/dotfiles/scripts/apt-update +!/.config/dotfiles/scripts/check-bin-pkg +!/.config/dotfiles/scripts/crlf-fix +!/.config/dotfiles/scripts/deb-src-export +!/.config/dotfiles/scripts/deb-ver-parse +!/.config/dotfiles/scripts/dperl +!/.config/dotfiles/scripts/dpkg-list-auto +!/.config/dotfiles/scripts/dpkg-list-installed +!/.config/dotfiles/scripts/dpkg-list-manual +!/.config/dotfiles/scripts/dpkg-list-martians +!/.config/dotfiles/scripts/dpkg-search +!/.config/dotfiles/scripts/dpkg-source-raw +!/.config/dotfiles/scripts/dpkg-which +!/.config/dotfiles/scripts/ensure-eof-empty-line +!/.config/dotfiles/scripts/from +!/.config/dotfiles/scripts/gpg-batch +!/.config/dotfiles/scripts/gpg-export +!/.config/dotfiles/scripts/gpg-sign-file +!/.config/dotfiles/scripts/gpg-warmup +!/.config/dotfiles/scripts/idle +!/.config/dotfiles/scripts/krd-debsrc +!/.config/dotfiles/scripts/krd-quilt +!/.config/dotfiles/scripts/krd-sbuild +!/.config/dotfiles/scripts/openssl-cert-auto-pem +!/.config/dotfiles/scripts/openssl-generate-dh-bundle +!/.config/dotfiles/scripts/openssl-ocsp +!/.config/dotfiles/scripts/pip-env +!/.config/dotfiles/scripts/quiet +!/.config/dotfiles/scripts/quilt-series-auto +!/.config/dotfiles/scripts/quilt-series-strip-comments +!/.config/dotfiles/scripts/run-as +!/.config/dotfiles/scripts/sperl +!/.config/dotfiles/scripts/static-compress +!/.config/dotfiles/scripts/to +!/.config/dotfiles/scripts/try-read-stdin +!/.config/dotfiles/scripts/ufind.sh +!/.config/dotfiles/scripts/zap-tree !/.config/dotfiles/woodpecker.yml !/.config/htop/htoprc.dist !/.config/mc/ini.dist @@ -22,16 +67,13 @@ !/.config/zsh/alias/diff.zsh !/.config/zsh/alias/directories.zsh !/.config/zsh/alias/git.zsh -!/.config/zsh/alias/gpg.zsh !/.config/zsh/alias/grep.zsh !/.config/zsh/alias/history.zsh -!/.config/zsh/alias/idle.zsh +!/.config/zsh/alias/idle.zsh.old !/.config/zsh/alias/k8s.zsh !/.config/zsh/alias/kconfig.zsh !/.config/zsh/alias/ls.zsh !/.config/zsh/alias/openwrt.zsh -!/.config/zsh/alias/quilt.zsh -!/.config/zsh/alias/sbuild.zsh !/.config/zsh/alias/sudo.zsh !/.config/zsh/alias/telnet.zsh !/.config/zsh/completion/.keep diff --git a/.config/dotfiles/install.sh b/.config/dotfiles/install.sh index fc17060..5f7c3cd 100755 --- a/.config/dotfiles/install.sh +++ b/.config/dotfiles/install.sh @@ -1,6 +1,11 @@ #!/bin/sh set -ef +unset LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE +unset LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS +unset LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION +unset POSIXLY_CORRECT TAR_OPTIONS + uri_krdsh="${GITKRDSH:-https://git.krd.sh/krd}/dotfiles" uri_github="${GITHUB:-https://github.com/rockdrilla}/dotfiles" @@ -9,9 +14,7 @@ git_branch='main' d_repo='.config/dotfiles/repo.git' f_gitignore='.config/dotfiles/gitignore' -have_cmd() { - command -v "$1" >/dev/null 2>&1 || return $? -} +have_cmd() { command -v "$1" /dev/null 2>&1 ; } fetch() { if have_cmd curl ; then diff --git a/.config/dotfiles/scripts/.keep b/.config/dotfiles/scripts/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/.config/dotfiles/scripts/_perl-wrapper.sh b/.config/dotfiles/scripts/_perl-wrapper.sh new file mode 100755 index 0000000..72ddebb --- /dev/null +++ b/.config/dotfiles/scripts/_perl-wrapper.sh @@ -0,0 +1,22 @@ +#!/bin/sh +set -ef + +e=perl ; d= +case "${0##*/}" in +d* ) + e=debugperl + + check-bin-pkg "$e:perl-debug" + + case "$1" in + -* ) + d=$1 ; shift + ;; + esac +;; +esac + +export PERL_HASH_SEED=0 PERL_PERTURB_KEYS=0 + +$e $d -T -c "$@" || echo +exec $e $d -T "$@" diff --git a/.config/dotfiles/scripts/_rsync-wrapper.sh b/.config/dotfiles/scripts/_rsync-wrapper.sh new file mode 100755 index 0000000..678fa86 --- /dev/null +++ b/.config/dotfiles/scripts/_rsync-wrapper.sh @@ -0,0 +1,45 @@ +#!/bin/sh +set -ef + +me="${0##*/}" +case "${me}" in +to | from ) ;; +* ) exit 1 ;; +esac + +check-bin-pkg rsync ssh:openssh-client + +h=$1 ; shift + +rsync_aux= +for i ; do + case "$i" in + -*) rsync_aux="${rsync_aux}${rsync_aux:+' '}$i" ;; + esac +done + +for i ; do + case "$i" in + -* ) continue ;; + *:* ) + k=${i#*:} + case "$k" in + *:* ) + env printf "%q: skipping bogus argument: %q\\n" "${me}" "$i" >&2 + continue + ;; + esac + i=${i%%:*} + ;; + * ) k=$i ;; + esac + + k="$h:$k" + + case "${me}" in + to ) src=$i dst=$k ;; + from ) src=$k dst=$i ;; + esac + + rsync -vaxISc -e ssh ${rsync_aux} "${src}" "${dst}" +done diff --git a/.config/dotfiles/scripts/apt-add-keyring b/.config/dotfiles/scripts/apt-add-keyring new file mode 100755 index 0000000..22d5698 --- /dev/null +++ b/.config/dotfiles/scripts/apt-add-keyring @@ -0,0 +1,430 @@ +#!/bin/sh +set -ef + +me="${0##*/}" + +usage() { + cat >&2 <<-EOF + # usage: ${me} [options] [keyring uri or path] + # options: + # -m, --merge + # merge with existing keyring (if any) + # -o, --optimize + # optimize keyring - preserve public key(s) which is/are used to sign repository + EOF + exit "${1:-0}" +} +[ $# != 0 ] || usage + +gnupg_testdata_clearsign() { + cat <<-EOF + -----BEGIN PGP SIGNED MESSAGE----- + Hash: SHA256 + + test data + -----BEGIN PGP SIGNATURE----- + + iHUEARYIAB0WIQTk95XBmO8PsqERxxdADlhK/AU7ugUCZVSqdwAKCRBADlhK/AU7 + uj4rAP4nb8tkF6FEhEd7sAoBBTPX5vc/WlAhDYM8uK61mo2KMQD/UjsJ3YOvRYip + epkflbk2cdCD7ZHsGYpBIhMh9eDTRQE= + =vrbd + -----END PGP SIGNATURE----- + EOF +} + +## NB: requires further "base64 -d" per line output +gnupg_dearmor_pubkey() { + sed -En -e "$(_gnupg_dearmor_pubkey_sed)" "$@" +} + +_gnupg_dearmor_pubkey_sed() { + cat <<-EOF + /^-----BEGIN PGP PUBLIC KEY BLOCK-----\$/ { + + :_scan + \$ { b _quit } + n + + /^-----BEGIN PGP PUBLIC KEY BLOCK-----\$/ { b _quit } + /^-----END PGP PUBLIC KEY BLOCK-----\$/ { b _quit } + + \\%^[[:alnum:]/+]{64}\$% { h ; b _read } + \\%^[[:alnum:]/+]{1,64}=*\$% { b _quit } + \\%^=[[:alnum:]/+]+=*\$% { b _quit } + + b _scan + + :_read + \$ { b _quit } + n + + /^-----BEGIN PGP PUBLIC KEY BLOCK-----\$/ { b _quit } + /^-----END PGP PUBLIC KEY BLOCK-----\$/ { z ; b _end } + + \\%^[[:alnum:]/+]{64}\$% { H ; b _read } + \\%^[[:alnum:]/+]{1,64}=*\$% { H ; b _read } + \\%^=[[:alnum:]/+]+=*\$% { H ; b _read } + b _read + + :_end + x + s/[\\n]//gm + s/^([[:alnum:]/+]+).+\$/\\1==/g + p + :_quit + } + EOF +} + +## NB: almost pristine format except last empty line +## TODO: research gnupg format +gnupg_armor_pubkey() { + cat <<-EOF + -----BEGIN PGP PUBLIC KEY BLOCK----- + + $(base64 < "$1") + + -----END PGP PUBLIC KEY BLOCK----- + EOF +} + +_gpgv_debug_pkt() { gpgv --debug 1 "$@" ; } + +gnupg_count_public_keys() { + ## sanity test + _gpgv_debug_pkt --version >/dev/null 2>&1 + + gnupg_testdata_clearsign \ + | _gpgv_debug_pkt --keyring "$1" 2>&1 >/dev/null \ + | { grep -Fw parse_packet || : ; } \ + | { grep -Fwc -e 'type=6' || : ; } +} + +# process options +n_opt=0 +for i ; do + case "$i" in + -m | --merge ) + if [ -n "${o_merge:+1}" ] ; then + env printf "%q: error: 'merge' flag already set\\n" "${me}" >&2 + usage 1 + fi + o_merge=1 + ;; + -o | --optimize ) + if [ -n "${o_optimize:+1}" ] ; then + env printf "%q: error: 'optimize' flag already set\\n" "${me}" >&2 + usage 1 + fi + o_optimize=1 + ;; + -*) + env printf "%q: error: unknown option: %q\\n" "${me}" "$i" >&2 + usage 1 + ;; + *) break ;; + esac + + n_opt=$((n_opt+1)) +done + +[ ${n_opt} = 0 ] || shift ${n_opt} + +arg_ok= +while : ; do + [ -n "$1" ] || break + [ -n "$2" ] || break + [ -n "$3" ] || break + arg_ok=1 ; break +done +[ -n "${arg_ok}" ] || usage 1 + +xsedx=$(printf '\027') + +_distro=$(sh -ec '. /etc/os-release ; printf "%s" "${ID}"' || : ) +_suite=$(sh -ec '. /etc/os-release ; printf "%s" "${VERSION_CODENAME}"' || : ) +if [ -z "${_distro}" ] || [ -z "${_suite}" ] ; then + env printf "%q: error: /etc/os-release is somewhat broken\\n" "${me}" >&2 + exit 1 +fi +unwrap_default_distro_suite() { + sed -ze "s${xsedx}@{distro}${xsedx}${_distro}${xsedx}g;s${xsedx}@{suite}${xsedx}${_suite}${xsedx}g" +} + +name=$(printf '%s' "$1" | unwrap_default_distro_suite) +uri=$(printf '%s' "$2" | unwrap_default_distro_suite) +suite=$(printf '%s' "$3" | unwrap_default_distro_suite) +keyring_arg=$(printf '%s' "$4" | unwrap_default_distro_suite) + +case "${name}" in +*/* | *..* ) + env printf "%q: error: name is path-like: %q\\n" "${me}" "${name}" >&2 + usage 1 +;; +esac + +verify_apt_uri() { + case "$1" in + http:* | https:* ) return 0 ;; + mirror:* | mirror+http:* | mirror+https:* ) return 0 ;; + esac + + __uncommon= + case "$1" in + cdrom:* | copy:* | file:* | rsh:* | ssh:* ) __uncommon=1 ;; + mirror+copy:* | mirror+file:* ) __uncommon=1 ;; + mirror+*:* ) + env printf "%q: error: unsupported uri mirror format: %q\\n" "${me}" "$1" >&2 + usage 1 + ;; + esac + + if [ -n "${__uncommon}" ] ; then + unset __uncommon + env printf "%q: warning: uncommon but likely valid uri: %q\\n" "${me}" "$1" >&2 + return 0 + fi + + env printf "%q: error: unsupported uri format: %q\\n" "${me}" "$1" >&2 + usage 1 +} + +verify_suite() { + if printf '%s' "$1" | grep -Eq '^[[:alnum:]][[:alnum:]-_]*[[:alnum:]]$' ; then + return 0 + fi + env printf "%q: error: unsupported suite format: %q\\n" "${me}" "$1" >&2 + usage 1 +} + +verify_apt_uri "${uri}" +verify_suite "${suite}" + +_keyring_dirs='/etc/apt/keyrings /etc/apt/trusted.gpg.d' +lookup_keyring_dir() { + if [ -n "${_keyring_dir}" ] ; then + echo "${_keyring_dir}" + return + fi + for __keyring_dir in ${_keyring_dirs} ; do + [ -d "${__keyring_dir}" ] || continue + _keyring_dir=${__keyring_dir} + unset __keyring_dir + echo "${_keyring_dir}" + return + # [ -z "${1:-}" ] || return + done + unset __keyring_dir + env printf "%q: error: APT installation is broken: /etc/apt/trusted.gpg.d/ is missing\\n" "${me}" >&2 + return 1 +} + +keyring_dir=$(lookup_keyring_dir) || exit 1 +keyring_base="${keyring_dir}/${name}" +keyring="${keyring_base}.asc" + +w=$(mktemp -d) ; : "${w:?}" + +lookup_keyring() { + case "${1:?}" in + */* | *..* ) + env printf "%q: error: arg is path-like: %q\\n" "${me}" "$1" >&2 + return 1 + ;; + esac + + __keyring_base=$(lookup_keyring_dir) || return 1 + __keyring_base="${__keyring_base}/$1" + __keyring_cnt=0 + for __ext in asc gpg gpg.asc ; do + __keyring="${__keyring_base}.${__ext}" + [ -e "${__keyring}" ] || continue + if ! [ -f "${__keyring}" ] ; then + __dentry_type=$(env stat -c %F "${__keyring}") + env printf "%q: warning: found %q (expected: file): %q\\n" "${me}" "${__dentry_type}" "${__keyring}" >&2 + continue + fi + if ! [ -s "${__keyring}" ] ; then + env printf "%q: warning: found empty file: %q\\n" "${me}" "${__keyring}" >&2 + continue + fi + env printf "%q: info: found keyring: %q\\n" "${me}" "${__keyring}" >&2 + echo "${__keyring}" + __keyring_cnt=$((__keyring_cnt+1)) + + ## return 1st available + [ -z "$2" ] || break + done + unset __keyring_base __ext __keyring __dentry_type +} + +if [ -n "${keyring_arg}" ] ; then + case "${keyring_arg}" in + http:* | https:* ) + apt-http-fetch "${keyring_arg}" "$w/keyring" || { + rm -rf "$w" + exit 1 + } + ;; + * ) + if ! [ -f "${keyring_arg}" ] ; then + env printf "%q: error: unknown keyring argument format: %q\\n" "${me}" "${keyring_arg}" >&2 + rm -rf "$w" + usage 1 + fi + if ! [ -s "${keyring_arg}" ] ; then + env printf "%q: error: empty keyring file: %q\\n" "${me}" "${keyring_arg}" >&2 + rm -rf "$w" + usage 1 + fi + cat < "${keyring_arg}" > "$w/keyring" + ;; + esac + + keynum=$(gnupg_count_public_keys "$w/keyring") + if [ "${keynum}" != 0 ] ; then + ## valid gnupg keyring file in (appropriate) binary format (*.gpg) + keyring="${keyring_base}.gpg" + else + while read -r n ; do + [ -n "$n" ] || continue + + echo "$n" | base64 -d > "$w/temp.gpg" || continue + x=$(gnupg_count_public_keys "$w/temp.gpg") + [ "$x" != 0 ] || continue + + keynum=$((keynum+1)) + done <<-EOF + $(gnupg_dearmor_pubkey "$w/keyring") + EOF + + rm -f "$w/temp.gpg" + fi + + if [ "${keynum}" = 0 ] ; then + env printf "%q: error: unable to parse keyring file: %q\\n" "${me}" "${keyring_arg}" >&2 + rm -rf "$w" + exit 1 + fi + + while [ -n "${o_merge}" ] ; do + lookup_keyring "${name}" | grep -Fxq -e "${keyring}" || break + + env printf "%q: warning: existing file is to be REPLACED: %q\\n" "${me}" "${keyring}" >&2 + env printf "%q: info: consider running script with -m/--merge option to merge with existing keyring\\n" "${me}" >&2 + break + done +fi + +## TODO: whether we can skip gpg-related tasks? + +## request gnupg for work +check-bin-pkg gpg gpgv dirmngr gpgconf + +gpg_on() { gpg-batch start ; } +gpg_off() { + cd / + gpg-batch stop + unset GNUPGHOME + rm -rf "$w" + exit "${1:-0}" +} + +( + export GNUPGHOME="$w/dot-gnupg" + mkdir -m 0700 "${GNUPGHOME}" + gpg_on + + cd "$w" + + csv_field_grep() { + if [ -n "$2" ] ; then + sed -En "\\${xsedx}$2${xsedx}p" + else + cat + fi \ + | cut -d: -f "$1" + } + + join_lines() { sed -zE 's/[[:space:]]+/ /g;s/^ //;s/ $//' ; } + + if [ -s ./keyring ] ; then + gpg --import ./keyring + rm -f ./keyring + else + apt-repo-get-fpr "${uri}" "${suite}" > fpr.repo + ## must contain at least one fingerprint; if not - bail out + [ -s fpr.repo ] + + keys=$(join_lines < fpr.repo) + + gpg --recv-keys ${keys} + fi + + gpg --with-colons --list-keys > dump.all + csv_field_grep 5 '^pub:' < dump.all > pub.all + + ## must contain at least one public key; if not - bail out + [ -s pub.all ] + + ## minimize keyring + while [ -n "${o_optimize}" ] ; do + csv_field_grep 10 '^fpr:' < dump.all > fpr.all + + : > fpr.save + for i in $(join_lines < pub.all) ; do + gpg --with-colons --list-keys "$i" \ + | csv_field_grep 10 '^fpr:' > "fpr.$i" + + ## must contain at least one fingerprint; if not - bail out + [ -s "fpr.$i" ] + + fpr_pub=$(head -n 1 "fpr.$i") + + ## try filter out useful fingerprints; if none - skip whole key chain + grep -Fx -f fpr.repo "fpr.$i" > "fpr-save.$i" || : + [ -s "fpr-save.$i" ] || continue + + if grep -Fxq -e "${fpr_pub}" "fpr-save.$i" ; then + ## primary key fingerprint match - do nothing + : + else + ## subkey fingerprint match - add primary key fingerprint + echo "${fpr_pub}" >> "fpr-save.$i" + fi + + ## append fingerprints + cat < "fpr-save.$i" >> fpr.save + done + ## must contain at least one fingerprint; if not - bail out + ## (supplied key material seems to be useless) + [ -s fpr.save ] + + sort -u < fpr.save > fpr.include + + grep -Fxv -f fpr.include fpr.all > fpr.exclude || : + if [ -s fpr.exclude ] ; then + gpg --delete-keys $(sed -E 's/$/!/' < fpr.exclude) + fi + + break + done + + if [ -n "${o_merge}" ] ; then + while read -r k ; do + [ -n "$k" ] || continue + + gpg --import "$k" || continue + rm -f "$k" + env printf "%q: info: merging with existing keyring: %q\\n" "${me}" "$k" >&2 + done <<-EOF + $(lookup_keyring "${name}" 2>/dev/null) + EOF + fi + + gpg --armor --export > "${keyring}" + gpg --show-keys "${keyring}" + + gpg_off + +) || gpg_off 1 diff --git a/.config/dotfiles/scripts/apt-env b/.config/dotfiles/scripts/apt-env new file mode 100755 index 0000000..a886c61 --- /dev/null +++ b/.config/dotfiles/scripts/apt-env @@ -0,0 +1,11 @@ +#!/bin/sh +set -ef + +set -a +DEBCONF_NONINTERACTIVE_SEEN=true +DEBIAN_FRONTEND=noninteractive +DEBIAN_PRIORITY=critical +TERM=linux +set +a + +exec "$@" diff --git a/.config/dotfiles/scripts/apt-http-fetch b/.config/dotfiles/scripts/apt-http-fetch new file mode 100755 index 0000000..cd01b05 --- /dev/null +++ b/.config/dotfiles/scripts/apt-http-fetch @@ -0,0 +1,51 @@ +#!/bin/sh +set -ef + +me="${0##*/}" +usage() { + cat >&2 <<-EOF + # usage: ${me} [options] + # cURL compatibility options: + # -s, --silent + # suppress output until error is occured + # -k, --insecure + # ignore TLS verification error + # -L, --location + # no-op (does nothing) + # --no-location + # prohibit HTTP redirects + EOF + exit "${1:-0}" +} +[ $# != 0 ] || usage + +o_quiet= +o_insecure= +o_noredir= + +## process options +n_opt=0 +for i ; do + case "$i" in + -s | --silent ) o_quiet=1 ;; + -k | --insecure ) o_insecure=1 ;; + --no-location ) o_noredir=1 ;; + -L | --location ) o_noredir= ;; + -* ) + env printf "%q: unknown option: %q\\n" "${me}" "$i" >&2 + usage 1 + ;; + * ) break ;; + esac + + n_opt=$((n_opt+1)) +done + +[ ${n_opt} = 0 ] || shift ${n_opt} +[ $# -eq 2 ] || usage 1 + +exec ${o_quiet:+ quiet } /usr/lib/apt/apt-helper \ + ${o_insecure:+ -o 'Acquire::https::Verify-Peer=false' } \ + ${o_insecure:+ -o 'Acquire::https::Verify-Host=false' } \ + ${o_noredir:+ -o 'Acquire::http::AllowRedirect=false' } \ + download-file "$1" "$2" diff --git a/.config/dotfiles/scripts/apt-install b/.config/dotfiles/scripts/apt-install new file mode 100755 index 0000000..1990200 --- /dev/null +++ b/.config/dotfiles/scripts/apt-install @@ -0,0 +1,9 @@ +#!/bin/sh +set -ef + +apt-update ${APT_OPTS} +exec apt-env \ + apt-get \ + ${APT_OPTS} \ + "${APT_INSTALL:-install}" \ + "$@" diff --git a/.config/dotfiles/scripts/apt-remove b/.config/dotfiles/scripts/apt-remove new file mode 100755 index 0000000..e72f150 --- /dev/null +++ b/.config/dotfiles/scripts/apt-remove @@ -0,0 +1,14 @@ +#!/bin/sh +set -ef + +apt-env \ + apt-get \ + ${APT_OPTS} \ + --allow-remove-essential \ + "${APT_REMOVE:-remove}" \ + "$@" +exec apt-env \ + apt-get \ + ${APT_OPTS} \ + -y + autoremove \ diff --git a/.config/dotfiles/scripts/apt-repo-get-fpr b/.config/dotfiles/scripts/apt-repo-get-fpr new file mode 100755 index 0000000..245df63 --- /dev/null +++ b/.config/dotfiles/scripts/apt-repo-get-fpr @@ -0,0 +1,80 @@ +#!/bin/sh +set -ef + +me="${0##*/}" +usage() { + cat >&2 <<-EOF + # usage: ${me} + EOF + exit "${1:-0}" +} +[ $# != 0 ] || usage + +arg_ok= +while : ; do + [ -n "$1" ] || break + [ -n "$2" ] || break + arg_ok=1 ; break +done +[ -n "${arg_ok}" ] || usage 1 + +check-bin-pkg gpg dirmngr gpgconf + +gpg_on() { gpg-batch start ; } +gpg_off() { + cd / + gpg-batch stop + unset GNUPGHOME + rm -rf "$w" + exit "${1:-0}" +} + +gpg_keys_from_sig() { + set +e + gpg --no-options --verify "$@" 2>&1 \ + | sed -En 's/\s+/ /g;s/ $//;/^([Gg][Pp][Gg]|[Gg][Nn][Uu][Pp][Gg]): using .+ key (.+)$/{s,,\2,p}' + set -e +} + +w=$(mktemp -d) ; : "${w:?}" +export GNUPGHOME="$w/dot-gnupg" + +( + mkdir -m 0700 "${GNUPGHOME}" + gpg_on + + cd "$w" + + while : ; do + apt-http-fetch -s "$1/dists/$2/InRelease" InRelease || break + gpg_keys_from_sig InRelease > apt.fpr.InRelease + break + done + rm -f InRelease + + while : ; do + apt-http-fetch -s "$1/dists/$2/Release.gpg" Release.gpg || break + apt-http-fetch -s "$1/dists/$2/Release" Release || break + gpg_keys_from_sig Release.gpg Release > apt.fpr.Release + break + done + rm -f Release.gpg Release + + rm -f apt.fpr + c=0 + for i in apt.fpr.InRelease apt.fpr.Release ; do + [ -s "$i" ] || continue + cp -f "$i" apt.fpr + c=$((c+1)) + done + + if [ "$c" = 2 ] ; then + cmp apt.fpr.InRelease apt.fpr.Release >&2 + fi + + [ -s apt.fpr ] + cat apt.fpr + + gpg_off + +) || gpg_off 1 diff --git a/.config/dotfiles/scripts/apt-search b/.config/dotfiles/scripts/apt-search new file mode 100755 index 0000000..58d63c4 --- /dev/null +++ b/.config/dotfiles/scripts/apt-search @@ -0,0 +1,52 @@ +#!/bin/sh +set -ef + +me="${0##*/}" +usage() { + cat >&2 <<-EOF + # usage: ${me} [regex] + EOF + exit "${1:-0}" +} +[ $# != 0 ] || usage +[ -n "$1" ] || usage 1 + +w=$(mktemp -d) ; : "${w:?}" +r=0 + +_exit() { + rm -rf "$w" + exit $r +} + +set +e +if [ "$(id -u)" = 0 ] ; then + apt-update +fi +apt-cache --names-only search "$1" > "$w/search" +r=$? + +[ $r = 0 ] || _exit +[ -s "$w/search" ] || _exit + +awk '{print $1}' < "$w/search" > "$w/list" +rm -f "$w/search" + +pat="${2:-$1}" + +## plain string, exact match +if grep -Fq -e "${pat}" "$w/list" ; then + grep -Fx -e "${pat}" "$w/list" + _exit +fi + +## regex (?) + +need_anchors= +case "${pat}" in +*^* | *\$* ) ;; +*) need_anchors=1 ;; +esac +grep -E${need_anchors:+x} -e "${pat}" "$w/list" + +_exit diff --git a/.config/dotfiles/scripts/apt-update b/.config/dotfiles/scripts/apt-update new file mode 100755 index 0000000..118ab6e --- /dev/null +++ b/.config/dotfiles/scripts/apt-update @@ -0,0 +1,53 @@ +#!/bin/sh +set -ef + +: "${DPKG_ADMINDIR:=/var/lib/dpkg}" + +find_fresh_ts() { + { + find "$@" -exec stat -c '%Y' '{}' '+' 2>/dev/null || : + ## duck and cover! + echo 1 + } | sort -rn | head -n 1 +} + +_apt_update() { + ## update package lists; may fail sometimes, + ## e.g. soon-to-release channels like Debian "bullseye" @ 22.04.2021 + + if [ $# = 0 ] ; then + ## (wannabe) smart package list update + ts_sources=$(find_fresh_ts /etc/apt/ -follow -regextype egrep -regex '^/etc/apt/sources\.list(|\.d/[^/]+\.(list|sources))$' -type f) + ts_lists=$(find_fresh_ts /var/lib/apt/lists/ -maxdepth 1 -regextype egrep -regex '^.+_Packages(|\.(bz2|gz|lz[4o]|xz|zstd?))$' -type f) + if [ ${ts_sources} -gt ${ts_lists} ] ; then + apt-env apt-get -y update + fi + else + apt-env apt-get "$@" update + fi +} + +_dpkg_avail_hack() { + set +e + suite=$(sh -ec '. /etc/os-release ; printf "%s" "${VERSION_CODENAME}"' || : ) + ## if ${suite} is empty then we're on Debian sid or so :) + : "${suite:-sid}" + set -e + f="${DPKG_ADMINDIR}/available" + case "${suite}" in + stretch | buster | bionic | focal ) + ## ref: https://unix.stackexchange.com/a/271387/49297 + if [ -s "$f" ] ; then + return + fi + echo '# fixing "dpkg/available"' >&2 + /usr/lib/dpkg/methods/apt/update "${DPKG_ADMINDIR}" apt apt + ;; + * ) + touch "$f" + ;; + esac +} + +_apt_update "$@" +_dpkg_avail_hack diff --git a/.config/dotfiles/scripts/check-bin-pkg b/.config/dotfiles/scripts/check-bin-pkg new file mode 100755 index 0000000..3bcd8ea --- /dev/null +++ b/.config/dotfiles/scripts/check-bin-pkg @@ -0,0 +1,44 @@ +#!/bin/sh +set -ef + +me="${0##*/}" +usage() { + cat >&2 <<-EOF + # usage: ${me} [:] [...] + EOF + exit "${1:-0}" +} +[ $# != 0 ] || usage +[ -n "$1" ] || usage 1 + +for i ; do + unset bin pkg + IFS=: read -r bin pkg <<-EOF + $i + EOF + + if [ -z "${bin}" ] ; then + env printf "%q: 'binary' expected but empty: %q\\n" "${me}" "$i" >&2 + usage 1 + fi +done + +for i ; do + unset bin pkg + IFS=: read -r bin pkg <<-EOF + $i + EOF + + [ -n "${pkg}" ] || pkg=${bin} + + if command -v "${bin}" >/dev/null 2>&1 ; then + continue + fi + + cat >&2 <<-EOF + # ${0##*/}: unable to find command '${bin}' + # install package '${pkg}' first, e.g. + # sudo apt-install ${pkg} + EOF + exit 1 +done diff --git a/.config/dotfiles/scripts/crlf-fix b/.config/dotfiles/scripts/crlf-fix new file mode 100755 index 0000000..5f3baf9 --- /dev/null +++ b/.config/dotfiles/scripts/crlf-fix @@ -0,0 +1,4 @@ +#!/bin/sh +set -ef + +exec sed -zE 's/\r\n/\n/g;s/\r/\n/g' "$@" diff --git a/.config/dotfiles/scripts/deb-src-export b/.config/dotfiles/scripts/deb-src-export new file mode 100755 index 0000000..b0cea0d --- /dev/null +++ b/.config/dotfiles/scripts/deb-src-export @@ -0,0 +1,254 @@ +#!/bin/sh +set -ef + +tar_opts='--blocking-factor=1 --format=gnu --no-selinux --no-xattrs --sparse' + +unset LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE +unset LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS +unset LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION +unset POSIXLY_CORRECT TAR_OPTIONS + +export LC_ALL=C.UTF-8 LANG=C.UTF8 + +have_cmd() { command -v "$1" /dev/null 2>&1 ; } + +ro_git() { GIT_OPTIONAL_LOCKS=0 command git "$@" ; } + +find_fast() { + find "$@" -printf . -quit | grep -Fq . +} + +check-bin-pkg dh:debhelper dpkg-parsechangelog:dpkg-dev + +have_git= +while [ "${DEB_SRC_EXPORT_GIT:-1}" = 1 ] ; do + have_cmd git || break + ro_git rev-parse --is-inside-work-tree >/dev/null 2>&1 || break + + have_git=1 + break +done + +want_gpg= +while [ "${DEB_SRC_EXPORT_GNUPG:-1}" = 1 ] ; do + have_cmd gpg || break + + [ -z "${DEB_SIGN_KEYID}" ] || want_gpg=1 + + if [ -n "${have_git}" ] ; then + if ro_git config --get commit.gpgsign | grep -Fxq true ; then + want_gpg=1 + fi + fi + + break +done + +SIGN_OPT= +if [ -n "${want_gpg}" ] ; then + sign_file() { gpg-sign-file "$@"; } +else + SIGN_OPT='--no-sign' + sign_file() { :; } + + unset \ + GNUPGHOME GPG_AGENT_INFO GPG_TTY PINENTRY_USER_DATA \ + SSH_AGENT_PID SSH_AUTH_SOCK \ + DEB_SIGN_KEYID DEBEMAIL DEBFULLNAME +fi + +deb_folder='debian' +if [ -z "$2" ] ; then + if [ -n "${have_git}" ] ; then + cd "$(ro_git rev-parse --show-toplevel)" + fi +else + deb_folder=$(printf '%s' "$2" | sed -zE 's,/+,/,g;s,/$,,') +fi + +## basic verification of debian/ folder +[ -d "${deb_folder}" ] +[ -s "${deb_folder}/rules" ] + +if [ "${deb_folder##*/}" = . ] ; then + deb_folder=$( ( cd "${deb_folder}" ; pwd ; ) ) +fi + +if [ -z "${SOURCE_DATE_EPOCH}" ] ; then + SOURCE_DATE_EPOCH=$(date -u +%s) +fi +export SOURCE_DATE_EPOCH + +temp_folder=$(mktemp -d) ; : "${temp_folder:?}" + +## prepare/warmup GnuPG +## if signing is disabled then it does nothing with files or GnuPG +date > "${temp_folder}/t" ; sign_file "${temp_folder}/t" ; rm -f "${temp_folder}/t" + +## (early) grab source package related files +mkdir -p "${temp_folder}/debian" +find "${deb_folder}/" -mindepth 1 -maxdepth 1 -printf '%P\0' \ +| tar -C "${deb_folder}" ${tar_opts} --dereference --null -T - -cf - \ +| tar -C "${temp_folder}/debian" -xf - + +chmod 0755 "${temp_folder}/debian/rules" + +d_rules() { DPKG_BUILD_API="${DPKG_BUILD_API:-0}" debian/rules "$@" ; } + +( + cd "${temp_folder}" + + set +e + + ## refresh control files + d_rules clean + d_rules debian/control + d_rules debian/changelog +) +if ! [ -s "${temp_folder}/debian/control" ] ; then + echo "# ${0##*/}: debian/control is missing/unreproducible for ${deb_folder}/!" + rm -rf "${temp_folder}" + exit 1 +fi + +## retrieve only desired fields from dpkg-parsechangelog output +if ! dpkg-parsechangelog -l "${temp_folder}/debian/changelog" > "${temp_folder}/changelog" ; then + rm -rf "${temp_folder}" + exit 1 +fi + +# pkg_name=$(deb822 get "${temp_folder}/changelog" Source | sed -zn 1p | tr -d '\0') +# pkg_ver=$(deb822 get "${temp_folder}/changelog" Version | sed -zn 1p | tr -d '\0') +# pkg_ts=$(deb822 get "${temp_folder}/changelog" Timestamp | sed -zn 1p | tr -d '\0') + +pkg_name=$(sed -En '/^Source:\s*(\S+)$/{s//\1/p;q;}' "${temp_folder}/changelog") +pkg_ver=$(sed -En '/^Version:\s*(\S+)$/{s//\1/p;q;}' "${temp_folder}/changelog") +pkg_ts=$(sed -En '/^Timestamp:\s*(\S+)$/{s//\1/p;q;}' "${temp_folder}/changelog") + +rm -f "${temp_folder}/changelog" + +SOURCE_DATE_EPOCH=${pkg_ts} +export SOURCE_DATE_EPOCH + +pkg_ver_list=$(deb-ver-parse "${pkg_ver}") +[ -n "${pkg_ver_list}" ] + +unset ver_epoch ver_upstream ver_revision +IFS='|' read -r ver_epoch ver_upstream ver_revision <&2 +else + echo "output directory: $T" >&2 + mkdir -p "$T" +fi + +if [ -z "${ver_upstream}" ] ; then + ## native package + ## do as simple as possible + W="$T/${pkg_name}-${ver_revision}" ; mkdir -p "$W" + echo "working directory (as subdirectory of output): $W" >&2 +else + ## regular package + ## do some tricks! :) + W=$(mktemp -d) + echo "working directory (auto-created): $W" >&2 +fi + +_cleanup() { + cd / + rm -rf -- "$W" ; rm -rf -- "$T" +} + +_finish_export() { + cd "$T" ; rm -rf -- "$W" + echo "index of $T" >&2 + ls -lhgG >&2 +} + +## move files to "permanent" location +tar -C "${temp_folder}" ${tar_opts} -cf - debian \ +| tar -C "$W" -xf - +rm -rf "${temp_folder}" ; unset temp_folder + +if [ -z "${ver_upstream}" ] ; then + ## native package + ## grab all the things (except Git) + ( + cd "${deb_folder}/.." + if [ -n "${have_git}" ] ; then + git ls-files -z + else + find ./ -mindepth 1 -maxdepth 1 -printf '%P\0' + fi \ + | sed -zE '/^(\.git|debian)(\/|$)/d' \ + | tar ${tar_opts} --null -T - -cf - \ + | tar -C "$W" -xf - + ) || { _cleanup ; exit 1 ; } +fi + +export TAR_OPTIONS="${tar_opts} --mtime @${pkg_ts} --sort=name --numeric-owner --owner=0 --group=0 --exclude-vcs" + +if [ -z "${ver_upstream}" ] ; then + ## native package + ## do as simple as possible + cd "$W" + + dpkg-buildpackage --build=source -z9 -d -nc ${SIGN_OPT} || { _cleanup ; exit 1 ; } + + _finish_export + exit +fi + +## regular package +## here goes magic and dragons + +## shortcuts +pnu="${pkg_name}_${ver_upstream}" +pnd="${pnu}-${ver_revision}" +pna="${pnd}_source" + +## try copy upstream source tarball(s) (if any) +( + cd "${deb_folder}/.." + + for d in '..' '.' ; do + ## main tarball + find "$d/" -follow -mindepth 1 -maxdepth 1 -type f -name "${pnu}.orig.*" \ + -exec cp -vaL -t "$T" {} + + ## extra tarball(s) for multiple upstream tarball (MUT) package + find "$d/" -follow -mindepth 1 -maxdepth 1 -type f -name "${pnu}.orig-*.*" \ + -exec cp -vaL -t "$T" {} + + done +) || { _cleanup ; exit 1 ; } + +cd "$W" + +( + ## verify that we have upstream tarball(s) + if ! find_fast "$T" -mindepth 1 -maxdepth 1 -name "${pnu}.orig.*" -type f ; then + d_rules debian/watch || : + check-bin-pkg uscan:devscripts || exit 1 + uscan --destdir "$T" --download-current-version --rename + fi + + tar -cf - debian | xz -9 > "$T/${pnd}.debian.tar.xz" + + ## hackish way to generate .dsc with minimal overhead + dpkg-source-raw "$T" || exit 1 + + sign_file "$T/${pnd}.dsc" + + dpkg-genbuildinfo --build=source -u"$T" -O > "$T/${pna}.buildinfo" + sign_file "$T/${pna}.buildinfo" + + dpkg-genchanges --build=source -u"$T" > "$T/${pna}.changes" + sign_file "$T/${pna}.changes" + + _finish_export + exit +) || { _cleanup ; exit 1 ; } diff --git a/.config/dotfiles/scripts/deb-ver-parse b/.config/dotfiles/scripts/deb-ver-parse new file mode 100755 index 0000000..8e83c72 --- /dev/null +++ b/.config/dotfiles/scripts/deb-ver-parse @@ -0,0 +1,23 @@ +#!/bin/sh +set +e ; set -f + +## debian package version handling + +## match pattern "epoch:version-revision" +m1='([1-9][0-9]*):([0-9][0-9a-zA-Z.+~-]*)-([0-9a-zA-Z.+~]+)' +## match pattern "epoch:native_version" +m2='([1-9][0-9]*):([0-9][0-9a-zA-Z.+~]*)' +## match pattern "version-revision" +m3='([0-9][0-9a-zA-Z.+~-]*)-([0-9a-zA-Z.+~]+)' +## match pattern "native_version" +m4='([0-9][0-9a-zA-Z.+~]*)' + +## replace pattern "epoch|upstream version|revision or native_version" +replace='\2\5|\3\7|\4\6\8\9' + +## sed script to split package version into kind of 'list' +pkg_ver_to_list='/^('${m1}'|'${m2}'|'${m3}'|'${m4}')$/{s##'${replace}'#;p;}' + +for i ; do + echo "$i" | sed -En "${pkg_ver_to_list}" +done diff --git a/.config/dotfiles/scripts/dperl b/.config/dotfiles/scripts/dperl new file mode 120000 index 0000000..e107f63 --- /dev/null +++ b/.config/dotfiles/scripts/dperl @@ -0,0 +1 @@ +_perl-wrapper.sh \ No newline at end of file diff --git a/.config/dotfiles/scripts/dpkg-list-auto b/.config/dotfiles/scripts/dpkg-list-auto new file mode 100755 index 0000000..5cd3d1d --- /dev/null +++ b/.config/dotfiles/scripts/dpkg-list-auto @@ -0,0 +1,49 @@ +#!/bin/sh +set -ef + +w=$(mktemp -d) ; : "${w:?}" + +have_cmd() { command -v "$1" /dev/null 2>&1 ; } + +unset has_mawk +if have_cmd mawk ; then has_mawk=1 ; fi +mawk_then_awk() { + if [ -n "${has_mawk}" ] + then mawk "$@" + else awk "$@" + fi +} + +f='/var/lib/apt/extended_states' +if [ -f "$f" ] ; then + mawk_then_awk ' + /^Package:/,/^$/ { + if ($1 == "Package:") { pkg = $2; } + if ($1 == "Architecture:") { arch = $2; } + if ($1 == "Auto-Installed:") { is_auto = $2; } + if ($0 == "") { + if (is_auto == 1) { print pkg ":" arch; } + } + } + ' "$f" | sort -V +fi > "$w/auto.pkg" + +while [ -s "$w/auto.pkg" ] ; do + dpkg-list-installed > "$w/all" + + ## fix: + ## /var/lib/apt/extended_states stores (some) arch:all entries as arch:native + sed -En '/^([^:]+):all$/ {s##/^\1:.+$/ {s//\1:all/}#p}' \ + < "$w/all" \ + > "$w/auto.sed" + + if [ -s "$w/auto.sed" ] ; then + sed -E -f "$w/auto.sed" + else + cat + fi < "$w/auto.pkg" + + break +done + +rm -rf "$w" diff --git a/.config/dotfiles/scripts/dpkg-list-installed b/.config/dotfiles/scripts/dpkg-list-installed new file mode 100755 index 0000000..8f93ba6 --- /dev/null +++ b/.config/dotfiles/scripts/dpkg-list-installed @@ -0,0 +1,6 @@ +#!/bin/sh +set -ef + +dpkg-query --show --showformat='${Package}:${Architecture}|${db:Status-Abbrev}\n' \ +| sed -En '/^(.+)\|[hi]i $/{s//\1/;p}' \ +| sort -V diff --git a/.config/dotfiles/scripts/dpkg-list-manual b/.config/dotfiles/scripts/dpkg-list-manual new file mode 100755 index 0000000..e407975 --- /dev/null +++ b/.config/dotfiles/scripts/dpkg-list-manual @@ -0,0 +1,15 @@ +#!/bin/sh +set -ef + +w=$(mktemp -d) ; : "${w:?}" + +dpkg-list-installed > "$w/all" +dpkg-list-auto > "$w/auto" + +if [ -s "$w/auto" ] ; then + grep -Fxv -f "$w/auto" +else + cat +fi < "$w/all" + +rm -rf "$w" diff --git a/.config/dotfiles/scripts/dpkg-list-martians b/.config/dotfiles/scripts/dpkg-list-martians new file mode 100755 index 0000000..16c119d --- /dev/null +++ b/.config/dotfiles/scripts/dpkg-list-martians @@ -0,0 +1,24 @@ +#!/bin/sh +set +e ; set -f + +_list() { + dpkg-query --show --showformat='${Package}:${Architecture} ${Version} status="${db:Status-Abbrev}"\n' \ + | grep -Ev '"ii "$' \ + | sort -V +} + +t=$(mktemp) +if [ -z "$t" ] ; then + # unable to create temporary file? + # produce "raw" output + _list + exit 0 +fi + +_list > "$t" +if [ -s "$t" ] ; then + echo '# "martian" packages (i.e. unusual state):' >&2 + cat "$t" +fi + +rm -f "$t" diff --git a/.config/dotfiles/scripts/dpkg-search b/.config/dotfiles/scripts/dpkg-search new file mode 100755 index 0000000..1c250fc --- /dev/null +++ b/.config/dotfiles/scripts/dpkg-search @@ -0,0 +1,25 @@ +#!/bin/sh +set -ef + +: "${1:?}" + +me="${0##*/}" + +if dpkg-query --search "$1" ; then exit 0 ; fi + +case "$1" in +*\** | *\?* ) + env printf "%q does not support globs: %q\\n" "${me}" "$1" >&2 + exit 1 +;; +esac + +while read -r f ; do + [ -n "$f" ] || continue + dpkg-query --search "$f" || continue + exit 0 +done </dev/null | grep -Fxv -e "$1" | sort -V) +EOF + +exit 1 diff --git a/.config/dotfiles/scripts/dpkg-source-raw b/.config/dotfiles/scripts/dpkg-source-raw new file mode 100755 index 0000000..037727b --- /dev/null +++ b/.config/dotfiles/scripts/dpkg-source-raw @@ -0,0 +1,280 @@ +#!/usr/bin/perl +# +# dpkg-source-raw: "hackish" script based on original dpkg-source +# repo: https://git.dpkg.org/git/dpkg/dpkg.git +# synced commit: f506e5dbc94393e9b5a8783d992815dca8ea7a2b +# file: scripts/dpkg-source.pl +# +# Copyright © 1996 Ian Jackson +# Copyright © 1997 Klee Dienes +# Copyright © 1999-2003 Wichert Akkerman +# Copyright © 1999 Ben Collins +# Copyright © 2000-2003 Adam Heath +# Copyright © 2005 Brendan O'Dea +# Copyright © 2006-2008 Frank Lichtenheld +# Copyright © 2006-2009,2012 Guillem Jover +# Copyright © 2008-2011 Raphaël Hertzog +# Copyright © 2020-2025 Konstantin Demin +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +use strict; +use warnings; + +use List::Util qw(any none); +use Cwd; +use File::Basename; +use File::Spec; + +use Dpkg (); +use Dpkg::ErrorHandling; +use Dpkg::Arch qw(:operators); +use Dpkg::Deps; +use Dpkg::Compression; +use Dpkg::Conf; +use Dpkg::Control::Info; +use Dpkg::Control::Tests; +use Dpkg::Control::Fields; +use Dpkg::Substvars; +use Dpkg::Version; +use Dpkg::Changelog::Parse; +use Dpkg::Source::Format; +use Dpkg::Source::Package; +use Dpkg::Vendor; + +# heavily-conditional "use MODULE" +BEGIN { + my $dpkg_ver = Dpkg::Version->new($Dpkg::PROGVERSION); + my $new_api_ver = Dpkg::Version->new('1.22.0'); + if ($dpkg_ver >= $new_api_ver) { + require Dpkg::Package; + Dpkg::Package->import(qw(set_source_name)); + # aliasing + *set_source_package = \&set_source_name; + } else { + require Dpkg::Vars; + Dpkg::Vars->import(qw(set_source_package)); + } +} + +my $build_format; +my %options = (); + +my $substvars = Dpkg::Substvars->new(); + +my @options; + +$options{origtardir} = $ARGV[0]; +$options{changelog_file} = 'debian/changelog'; + +my %ch_options = (file => $options{changelog_file}); +my $changelog = changelog_parse(%ch_options); +my $control = Dpkg::Control::Info->new('debian/control'); + +# +$ENV{SOURCE_DATE_EPOCH} ||= $changelog->{timestamp} || time; + +# Select the format to use +if (not defined $build_format) { + my $format_file = 'debian/source/format'; + if (-e $format_file) { + my $format = Dpkg::Source::Format->new(filename => $format_file); + $build_format = $format->get(); + } else { + warning('no source format specified in %s, ' . + 'see dpkg-source(1)', $format_file); + $build_format = '1.0'; + } +} + +my $srcpkg = Dpkg::Source::Package->new(format => $build_format, + options => \%options); +my $fields = $srcpkg->{fields}; + +my @sourcearch; +my %archadded; +my @binarypackages; + +# Scan control info of source package +my $src_fields = $control->get_source(); +error("debian/control doesn't contain any information about the source package") unless defined $src_fields; +my $src_sect = $src_fields->{'Section'} || 'unknown'; +my $src_prio = $src_fields->{'Priority'} || 'unknown'; + +foreach my $f (keys %{$src_fields}) { + my $v = $src_fields->{$f}; + + if ($f eq 'Source') { + set_source_package($v); + $fields->{$f} = $v; + } elsif ($f eq 'Uploaders') { + # Merge in a single-line + ($fields->{$f} = $v) =~ s/\s*[\r\n]\s*/ /g; + } elsif (any { $f eq $_ } field_list_src_dep()) { + my $dep; + my $type = field_get_dep_type($f); + $dep = deps_parse($v, build_dep => 1, union => $type eq 'union'); + error('error occurred while parsing %s', $f) unless defined $dep; + my $facts = Dpkg::Deps::KnownFacts->new(); + $dep->simplify_deps($facts); + $dep->sort() if $type eq 'union'; + $fields->{$f} = $dep->output(); + } else { + field_transfer_single($src_fields, $fields, $f); + } +} + +# Scan control info of binary packages +my @pkglist; +foreach my $pkg ($control->get_packages()) { + my $p = $pkg->{'Package'}; + my $sect = $pkg->{'Section'} || $src_sect; + my $prio = $pkg->{'Priority'} || $src_prio; + my $type = $pkg->{'Package-Type'} || + $pkg->get_custom_field('Package-Type') || 'deb'; + my $arch = $pkg->{'Architecture'}; + my $profile = $pkg->{'Build-Profiles'}; + + my $pkg_summary = sprintf('%s %s %s %s', $p, $type, $sect, $prio); + + $pkg_summary .= ' arch=' . join ',', split ' ', $arch; + + if (defined $profile) { + # Instead of splitting twice and then joining twice, we just do + # simple string replacements: + + # Remove the enclosing <> + $profile =~ s/^\s*<(.*)>\s*$/$1/; + # Join lists with a plus (OR) + $profile =~ s/>\s+{'Protected'} and $pkg->{'Protected'} eq 'yes') { + $pkg_summary .= ' protected=yes'; + } + if (defined $pkg->{'Essential'} and $pkg->{'Essential'} eq 'yes') { + $pkg_summary .= ' essential=yes'; + } + + push @pkglist, $pkg_summary; + push @binarypackages, $p; + foreach my $f (keys %{$pkg}) { + my $v = $pkg->{$f}; + if ($f eq 'Architecture') { + # Gather all binary architectures in one set. 'any' and 'all' + # are special-cased as they need to be the only ones in the + # current stanza if present. + if (debarch_eq($v, 'any') || debarch_eq($v, 'all')) { + push(@sourcearch, $v) unless $archadded{$v}++; + } else { + for my $a (split(/\s+/, $v)) { + error("'%s' is not a legal architecture string", $a) + if debarch_is_illegal($a); + error('architecture %s only allowed on its ' . + "own (list for package %s is '%s')", + $a, $p, $a) + if $a eq 'any' or $a eq 'all'; + push(@sourcearch, $a) unless $archadded{$a}++; + } + } + } elsif (any { $f eq $_ } qw(Homepage Description)) { + # Do not overwrite the same field from the source entry + } else { + field_transfer_single($pkg, $fields, $f); + } + } +} + +unless (scalar(@pkglist)) { + error("debian/control doesn't list any binary package"); +} + +if (any { $_ eq 'any' } @sourcearch) { + # If we encounter one 'any' then the other arches become insignificant + # except for 'all' that must also be kept + if (any { $_ eq 'all' } @sourcearch) { + @sourcearch = qw(any all); + } else { + @sourcearch = qw(any); + } +} else { + # Minimize arch list, by removing arches already covered by wildcards + my @arch_wildcards = grep { debarch_is_wildcard($_) } @sourcearch; + my @mini_sourcearch = @arch_wildcards; + foreach my $arch (@sourcearch) { + if (none { debarch_is($arch, $_) } @arch_wildcards) { + push @mini_sourcearch, $arch; + } + } + @sourcearch = @mini_sourcearch; +} +$fields->{'Architecture'} = join(' ', @sourcearch); +$fields->{'Package-List'} = "\n" . join("\n", sort @pkglist); + +# Scan fields of dpkg-parsechangelog +foreach my $f (keys %{$changelog}) { + my $v = $changelog->{$f}; + + if ($f eq 'Source') { + set_source_package($v); + $fields->{$f} = $v; + } elsif ($f eq 'Version') { + my ($ok, $error) = version_check($v); + error($error) unless $ok; + $fields->{$f} = $v; + } elsif ($f eq 'Binary-Only') { + error('building source for a binary-only release') + if $v eq 'yes'; + } elsif ($f eq 'Maintainer') { + # Do not replace the field coming from the source entry + } else { + field_transfer_single($changelog, $fields, $f); + } +} + +$fields->{'Binary'} = join(', ', @binarypackages); +# Avoid overly long line by splitting over multiple lines +if (length($fields->{'Binary'}) > 980) { + $fields->{'Binary'} =~ s/(.{0,980}), ?/$1,\n/g; +} + +my $basenamerev = $srcpkg->get_basename(1); + +# fragment from Dpkg::Source::Package::V2::_generate_patch() + my $tarfile; + my $comp_ext_regex = compression_get_file_extension_regex(); + foreach my $file (sort $srcpkg->find_original_tarballs()) { + if ($file =~ /\.orig\.tar\.$comp_ext_regex$/) { + if (defined($tarfile)) { + error('several orig.tar files found (%s and %s) but only ' . + 'one is allowed', $tarfile, $file); + } + $srcpkg->add_file($file); + } elsif ($file =~ /\.orig-([[:alnum:]-]+)\.tar\.$comp_ext_regex$/) { + $srcpkg->add_file($file); + } + } +# end of fragment + +# fragment from Dpkg::Source::Package::V2::do_build() + $srcpkg->add_file("$ARGV[0]/$basenamerev.debian.tar.xz"); +# end of fragment + +# Write the .dsc +my $dscname = "$ARGV[0]/$basenamerev.dsc"; +$srcpkg->write_dsc(filename => $dscname, substvars => $substvars); +exit(0); diff --git a/.config/dotfiles/scripts/dpkg-which b/.config/dotfiles/scripts/dpkg-which new file mode 100755 index 0000000..51835a0 --- /dev/null +++ b/.config/dotfiles/scripts/dpkg-which @@ -0,0 +1,91 @@ +#!/bin/sh +set +e ; set -f + +usage() { + cat >&2 <<-EOF + # usage: ${0##*/} [ ..] + EOF + exit "${1:-0}" +} +[ $# != 0 ] || usage + +: "${DPKG_ADMINDIR:=/var/lib/dpkg}" +dpkg_info_dir="${DPKG_ADMINDIR}/info" +dpkg_diversions="${DPKG_ADMINDIR}/diversions" + +# _same() { find "$@" -maxdepth 0 -samefile "$1" -print0 2>/dev/null ; } + +_get_alt() { + [ -n "$1" ] || return + + a="$1" + case "$1" in + /usr/local/* ) return ;; + /usr/* ) a="${1#/usr}" ;; + /* ) a="/usr$1" ;; + * ) return ;; + esac + + # n=$(_same "$1" "$a" | xargs -0 sh -c 'echo $#' --) + # [ "$n" = 2 ] || return + # printf '%s' "$a" + test "$1" -ef "$a" || return 0 + printf '%s' "$a" +} + +_dpkg_search() { + [ -n "$1" ] || return + + grep -FxRl -e "$1" "${dpkg_info_dir}/" \ + | sed -En '/\.list$/{s,^.+/([^/]+)\.list$,\1,p}' +} + +_dpkg_divert() { + [ -n "$1" ] || return + + n=$(grep -Fxhn -e "$1" "${dpkg_diversions}" | cut -d: -f1) + [ -n "$n" ] || return + + case "$((n%3))" in + 1 | 2 ) ;; + * ) return ;; + esac + + k=$(( n - (n%3) )) + divert_pkg=$(sed -n "$((k+3))p" "${dpkg_diversions}") + case "$((n%3))" in + 1 ) + [ "${divert_pkg}" = ':' ] || echo "${divert_pkg}" + ;; + 2 ) + divert_from=$(sed -n "$((k+1))p" "${dpkg_diversions}") + _dpkg_search "${divert_from}" | grep -Fxv -e "${divert_pkg}" + ;; + esac +} + +for i ; do + [ -n "$i" ] || continue + + name= + altpath=$(_get_alt "$i") + + name=$(_dpkg_divert "$i") + if [ -n "${name}" ] ; then + echo "${name}" + continue + fi + + name=$(_dpkg_divert "${altpath}") + if [ -n "${name}" ] ; then + echo "${name}" + continue + fi + + name=$( { _dpkg_search "$i" ; _dpkg_search "${altpath}" ; } | sort -uV) + if [ -n "${name}" ] ; then + echo "${name}" + continue + fi +done +exit 0 diff --git a/.config/dotfiles/scripts/ensure-eof-empty-line b/.config/dotfiles/scripts/ensure-eof-empty-line new file mode 100755 index 0000000..fb260fc --- /dev/null +++ b/.config/dotfiles/scripts/ensure-eof-empty-line @@ -0,0 +1,35 @@ +#!/bin/sh +set -ef + +[ -n "$1" ] || exit 1 +[ -f "$1" ] || exit 1 +! [ -h "$1" ] || exit 1 + +# ensure that we have at least two empty lines at the end +n=$(env stat -c '%s' "$1") || exit 1 + +want_lf=2 i=$n +case "$n" in +1 ) ;; +0 ) exit 0 ;; +* ) i=2 ;; +esac + +while [ $i -gt 0 ] ; do + n=$((n - 1)) + c=$(od -A n -j "$n" -N 1 -t x1 "$1") || exit 1 + c=$(printf '%s' "$c" | tr -d '[:space:]') + i=$((i - 1)) + + [ "$c" = '0a' ] || break + want_lf=$((want_lf - 1)) +done + +padding= +case "${want_lf}" in +0 ) exit 0 ;; +1 ) padding='\n' ;; +2 ) padding='\n\n' ;; +esac + +printf "${padding}" >> "$1" diff --git a/.config/dotfiles/scripts/from b/.config/dotfiles/scripts/from new file mode 120000 index 0000000..5f75b49 --- /dev/null +++ b/.config/dotfiles/scripts/from @@ -0,0 +1 @@ +_rsync-wrapper.sh \ No newline at end of file diff --git a/.config/dotfiles/scripts/gpg-batch b/.config/dotfiles/scripts/gpg-batch new file mode 100755 index 0000000..9f9d22f --- /dev/null +++ b/.config/dotfiles/scripts/gpg-batch @@ -0,0 +1,59 @@ +#!/bin/sh +set -ef + +: "${GPG_KEYSERVER:=hkps://keyserver.ubuntu.com}" + +me="${0##*/}" +usage() { + cat >&2 <<-EOF + # usage: ${me} {start|stop} + # NB: ensure that env GNUPGHOME is set to appropriate (e.g. temporary) directory + EOF + exit "${1:-0}" +} +[ $# != 0 ] || usage + +act=$(printf '%s' "$1" | tr '[:upper:]' '[:lower:]') +case "${act}" in +1 | start ) ;; +0 | stop ) ;; +* ) usage 1 ;; +esac + +check-bin-pkg gpg dirmngr gpgconf + +case "${act}" in +1 | start ) + [ -n "${GNUPGHOME}" ] || usage 1 + [ -d "${GNUPGHOME}" ] || usage 1 + + cd "${GNUPGHOME}" + cat > gpg.conf <<-EOF + quiet + batch + trust-model always + no-auto-check-trustdb + ignore-time-conflict + keyid-format 0xlong + keyserver ${GPG_KEYSERVER} + EOF + cat > dirmngr.conf <<-EOF + quiet + batch + keyserver ${GPG_KEYSERVER} + EOF + quiet gpg --update-trustdb + quiet gpg --list-keys + quiet dirmngr +;; +0 | stop ) + [ -n "${GNUPGHOME}" ] || exit 0 + [ -d "${GNUPGHOME}" ] || usage 1 + + cd "${GNUPGHOME}" + quiet gpgconf --kill all + cd / + rm -rf "${GNUPGHOME}" +;; +esac +exit 0 diff --git a/.config/dotfiles/scripts/gpg-export b/.config/dotfiles/scripts/gpg-export new file mode 100755 index 0000000..c8157d1 --- /dev/null +++ b/.config/dotfiles/scripts/gpg-export @@ -0,0 +1,29 @@ +#!/bin/sh +set -ef + +: "${1:?}" "${2:?}" + +w=$(mktemp -d) ; : "${w:?}" + +gpg_on() { gpg-batch start ; } +gpg_off() { + cd / + gpg-batch stop + unset GNUPGHOME + rm -rf "$w" + exit "${1:-0}" +} + +( + export GNUPGHOME="$w/dot-gnupg" + mkdir -m 0700 "${GNUPGHOME}" + gpg_on + + gpg --import "$1" + gpg --armor --export > "$w/export" + cat < "$w/export" > "$2" + gpg --show-keys "$2" + + gpg_off + +) || gpg_off 1 diff --git a/.config/dotfiles/scripts/gpg-sign-file b/.config/dotfiles/scripts/gpg-sign-file new file mode 100755 index 0000000..fe92a18 --- /dev/null +++ b/.config/dotfiles/scripts/gpg-sign-file @@ -0,0 +1,38 @@ +#!/bin/sh +set +e ; set -f + +me="${0##*/}" + +## test that 1st argument is (exactly) file +if [ -z "$1" ] ; then + env printf "%q: requires 1st argument\\n" "${me}" >&2 + exit 1 +fi +if [ -h "$1" ] ; then + env printf "%q: symlinks are not supported: %q\\n" "${me}" "$1" >&2 + exit 1 +fi +if ! [ -f "$1" ] ; then + env printf "%q: file does not exist: %q\\n" "${me}" "$1" >&2 + exit 1 +fi + +check-bin-pkg gpg dirmngr gpgconf + +w=$(mktemp -d) ; : "${w:?}" +( + set -e + cp -L "$1" "$w/s" + ensure-eof-empty-line "$w/s" + gpg \ + --utf8-strings --textmode --armor --clearsign \ + --output "$w/d" - < "$w/s" + rm -f "$w/s" + cat < "$w/d" > "$1" + rm -f "$w/d" +) +res=$? + +rm -rf -- "$w" + +exit ${res} diff --git a/.config/dotfiles/scripts/gpg-warmup b/.config/dotfiles/scripts/gpg-warmup new file mode 100755 index 0000000..370f6d4 --- /dev/null +++ b/.config/dotfiles/scripts/gpg-warmup @@ -0,0 +1,10 @@ +#!/bin/sh +set +e ; set -f + +check-bin-pkg gpg || exit $? + +t=$(mktemp) || exit $? +gpg -abs "$t" +r=$? +rm -f "$t" "$t.asc" +exit $r diff --git a/.config/dotfiles/scripts/idle b/.config/dotfiles/scripts/idle new file mode 100755 index 0000000..eb2d1e7 --- /dev/null +++ b/.config/dotfiles/scripts/idle @@ -0,0 +1,20 @@ +#!/bin/sh +set +e ; set -f + +have_cmd() { command -v "$1" /dev/null 2>&1 ; } + +has_nice=1 has_chrt=1 has_ionice=1 +have_cmd nice || has_nice= +have_cmd chrt || has_chrt= +have_cmd ionice || has_ionice= + +if [ -z "${has_nice}${has_chrt}${has_ionice}" ] ; then + env printf "%q: unable to launch command in idle mode due to missing executables\\n" "${0##*/}" >&2 + exit 126 +fi + +exec \ + ${has_nice:+ nice -n +40 } \ + ${has_chrt:+ chrt -i 0 } \ + ${has_ionice:+ ionice -c 3 } \ +"$@" diff --git a/.config/dotfiles/scripts/krd-debsrc b/.config/dotfiles/scripts/krd-debsrc new file mode 100755 index 0000000..cb009d1 --- /dev/null +++ b/.config/dotfiles/scripts/krd-debsrc @@ -0,0 +1,13 @@ +#!/bin/sh +set +e ; set -f + +[ -n "${1:?}" ] + +check-bin-pkg deb-src-export || exit $? + +case "$1" in +*/* ) dstdir="$1/src" ;; +* ) dstdir="/tmp/$1/src" ;; +esac +rm -rf "${dstdir}" +exec deb-src-export "${dstdir}" diff --git a/.config/dotfiles/scripts/krd-quilt b/.config/dotfiles/scripts/krd-quilt new file mode 100755 index 0000000..a1616d9 --- /dev/null +++ b/.config/dotfiles/scripts/krd-quilt @@ -0,0 +1,60 @@ +#!/bin/sh +set +e ; set -f + +check-bin-pkg quilt || exit $? + +[ -n "${1:?}" ] || exit 1 + +if [ -d "$1" ] ; then + patchdir="$1/debian/patches" + if [ -d "${patchdir}" ] ; then + ls "${patchdir}/series" >/dev/null || exit 1 + else + patchdir="$1" + fi + + series="${patchdir}/series" + if ! [ -f "${series}" ] ; then + mkdir -p "$1/.pc" || exit 1 + series="$1/.pc/krd-quilt-series" + touch "${series}" || exit 1 + quilt-series-auto "${patchdir}" > "${series}" + fi +elif [ -f "$1" ] ; then + [ -s "$1" ] || exit 1 + + series="$1" + patchdir=$(dirname "${series}") +else + exit 1 +fi + +set -a +QUILT_SERIES="${series:?}" +QUILT_PATCHES="${patchdir:?}" +set +a + +xsedx=$(printf '\027') + +r=0 +while read -r i ; do + [ -n "$i" ] || continue + + quilt --fuzz=0 push "$i" || exit $? + quilt refresh "$i" || exit $? + + sed -E -i \ + -e 's#^(-{3} )[^/][^/]*/(.*)$#\1a/\2#;' \ + -e 's#^(\+{3} )[^/][^/]*/(.*)$#\1b/\2#' \ + "$i" + + rm -f "$i"'~' +done <&2 <<-EOF + # usage: ${me} {|:} [env|arguments] + EOF + exit "${1:-0}" +} +[ $# != 0 ] || usage + +check-bin-pkg sbuild xz:xz-utils || exit $? + +_tmpdir() { + TMPDIR="$1" + TMP="$1" + TEMPDIR="$1" + TEMP="$1" + export TMPDIR TMP TEMPDIR TEMP +} +## TODO: questionable +_tmpdir /tmp + +[ -n "${1:?}" ] +[ -n "${2:?}" ] + +case "$1" in +*/* ) topdir="$1" ;; +* ) topdir="/tmp/$1" ;; +esac +[ -d "${topdir}" ] || return 1 + +srcdir="${topdir}/src" +[ -d "${srcdir}" ] || return 2 + +_sbuild_args= +_append_args() { + for __i ; do + _sbuild_args="${_sbuild_args}${_sbuild_args:+ }$(env printf '%q' "${__i}" )" + done ; unset __i +} + +case "$2" in +*:* ) + unset host_arch build_arch _xtra + IFS=: read -r host_arch build_arch _xtra <<-EOF + $i + EOF + + if [ -z "${host_arch}" ] ; then + env printf "%q: error: 'host-arch' expected but empty\\n" "${me}" >&2 + exit 1 + fi + if [ -z "${build_arch}" ] ; then + env printf "%q: error: 'build-arch' expected but empty\\n" "${me}" >&2 + exit 1 + fi + if [ -n "${_xtra}" ] ; then + env printf "%q: warning: extra data in 'host-arch:build-arch' specifier: %q\\n" "${me}" "${_xtra}" >&2 + fi + unset _xtra + + arch=${host_arch} + _append_args "--host=${host_arch}" + _append_args "--build=${build_arch}" +;; +* ) + arch=$2 + _append_args "--arch=$2" +;; +esac + +## done with args +shift 2 + +for i ; do + ## naive splitting args and env + case "$i" in + -* ) _append_args "$i" ;; + =* ) _append_args "$i" ;; + *= ) unset "$i" ;; + *=* ) export "$i" ;; + * ) _append_args "$i" ;; + esac +done ; unset i + +builddir="${topdir}/${arch}" +mkdir -p "${topdir}/all" "${builddir}" "${builddir}-all" "${builddir}-debug" + +cd "${builddir}" + +set +f +for i in "${srcdir}"/*.dsc ; do + ## I'm NOT proud of using "eval" here :( + eval "idle sbuild --arch-all --arch-any ${_sbuild_args} $i" || exit $? + find -name '*.build' -type l -exec rm -f {} + + find -name '*.build' -type f -exec xz -9vv {} + +done + +find \ + -regextype egrep -regex '.+all\.d?deb$' -type f \ + -exec mv -fvt "../${arch}-all" {} + + +find \ + -name '*.ddeb' -type f \ + -exec mv -fvt "../${arch}-debug" {} + +find \ + -regextype egrep -regex '.+dbgsym_[^_]+_'"${arch}"'\.d?deb$' -type f \ + -exec mv -fvt "../${arch}-debug" {} + + +cd "${builddir}-all" +find -type f -exec mv -nvt '../all' {} + + +exit 0 diff --git a/.config/dotfiles/scripts/openssl-cert-auto-pem b/.config/dotfiles/scripts/openssl-cert-auto-pem new file mode 100755 index 0000000..85df7a5 --- /dev/null +++ b/.config/dotfiles/scripts/openssl-cert-auto-pem @@ -0,0 +1,117 @@ +#!/bin/sh +set +e ; set -f + +me=${0##*/} +usage() { + cat >&2 <<-EOF + # usage: ${me} [PEM output file] [fingerprints output file] [offsets output file] + EOF + exit "${1:-0}" +} +[ $# != 0 ] || usage + +[ -n "$1" ] || exit 1 +[ -f "$1" ] || { + env printf '%s: not a file or does not exist: %q\n' "${me}" "$1" >&2 + exit 1 +} +[ -s "$1" ] || exit 0 + +w=$(mktemp -d) || exit 1 +w_cleanup() { + [ -z "$w" ] || ls -lA "$w/" >&2 + [ -z "$w" ] || rm -rf "$w" + unset w + exit "${1:-0}" +} + +have_cmd() { command -v "$1" /dev/null 2>&1 ; } + +unset has_mawk +if have_cmd mawk ; then has_mawk=1 ; fi +mawk_then_awk() { + if [ -n "${has_mawk}" ] + then mawk "$@" + else awk "$@" + fi +} + +bundle_offsets() { + mawk_then_awk 'BEGIN { OFS = ","; i_begin = 0; } + $0 == "-----BEGIN CERTIFICATE-----" { + i_begin = NR; + } + $0 == "-----END CERTIFICATE-----" { + if (i_begin > 0) { + print i_begin, NR; + i_begin = 0; + } + }' "$1" +} + +bundle_fingerprints() { + local x f + while read -r x ; do + [ -n "$x" ] || continue + + f=$(sed -ne "${x}p" "$1" | openssl x509 -noout -fingerprint -sha256) + [ -n "$f" ] || f=$(sed -ne "${x}p" "$1" | openssl x509 -noout -fingerprint) + [ -n "$f" ] || continue + + printf '%s\n' "$f" | tr '[:upper:]' '[:lower:]' + done < "$2" +} + +openssl storeutl -certs "$1" > "$w/cert.pem" || w_cleanup 1 +[ -s "$w/cert.pem" ] || w_cleanup 1 +tr -s '\r\n' '\n' < "$w/cert.pem" > "$w/cert.txt" +[ -s "$w/cert.txt" ] || w_cleanup 1 +rm -f "$w/cert.pem" + +bundle_offsets "$w/cert.txt" > "$w/cert.off" +[ -s "$w/cert.off" ] || w_cleanup 1 + +bundle_fingerprints "$w/cert.txt" "$w/cert.off" > "$w/cert.fp.all" +[ -s "$w/cert.fp.all" ] || w_cleanup 1 + +sort -uV < "$w/cert.fp.all" > "$w/cert.fp" +while read -r fp ; do + [ -n "${fp}" ] || continue + + n=$(grep -m1 -Fxn -e "${fp}" "$w/cert.fp.all" | cut -d : -f 1) + [ -n "$n" ] || continue + + off=$(sed -ne "${n}p" "$w/cert.off") + [ -n "${off}" ] || continue + + sed -ne "${off}p" "$w/cert.txt" +done < "$w/cert.fp" > "$w/cert.pem" +[ -s "$w/cert.pem" ] || w_cleanup 1 +rm -f "$w/cert.txt" "$w/cert.off" "$w/cert.fp.all" + +if [ -n "$2" ] ; then + while : ; do + if [ -e "$2" ] ; then + [ -f "$2" ] || break + fi + cat > "$2" + break ; done +else + cat +fi < "$w/cert.pem" + +while [ -n "$3" ] ; do + if [ -e "$3" ] ; then + [ -f "$3" ] || break + fi + cat "$w/cert.fp" > "$3" +break ; done + +while [ -n "$4" ] ; do + if [ -e "$4" ] ; then + [ -f "$4" ] || break + fi + bundle_offsets "$w/cert.pem" > "$4" +break ; done + +rm -rf "$w" ; unset w diff --git a/.config/dotfiles/scripts/openssl-generate-dh-bundle b/.config/dotfiles/scripts/openssl-generate-dh-bundle new file mode 100755 index 0000000..f54cc1f --- /dev/null +++ b/.config/dotfiles/scripts/openssl-generate-dh-bundle @@ -0,0 +1,28 @@ +#!/bin/sh +set -ef + +for k in 1024:20 2048:80 ; do + + unset b t + IFS=':' read -r b t <<-EOF + $k + EOF + + f="dh${b}.pem" + echo "# openssl genpkey: $f" >&2 + timeout --kill-after=3s ${t}s \ + openssl genpkey -quiet -genparam -algorithm DH -out "./$f" -pkeyopt "dh_paramgen_prime_len:${b}" +done + +for k in 2048:80 3072:120 4096:160 ; do + + unset b t + IFS=':' read -r b t <<-EOF + $k + EOF + + f="ffdhe${b}.pem" + echo "# openssl genpkey: $f" >&2 + timeout --kill-after=3s ${t}s \ + openssl genpkey -quiet -genparam -algorithm DH -out "./$f" -pkeyopt "group:ffdhe${b}" +done diff --git a/.config/dotfiles/scripts/openssl-ocsp b/.config/dotfiles/scripts/openssl-ocsp new file mode 100755 index 0000000..352a82c --- /dev/null +++ b/.config/dotfiles/scripts/openssl-ocsp @@ -0,0 +1,209 @@ +#!/bin/sh +set -ef + +ocsp_fetch_timeout=15 +ocsp_fetch_retries=3 +ocsp_fetch_retry_delay=5 +ocsp_valid_threshold=86400 + +me="${0##*/}" +usage() { + cat >&2 <<-EOF + # usage: ${me} [args...] + # ${me} get-uri + # ${me} is-valid + # ${me} is-expiring + # ${me} fetch + EOF + exit "${1:-0}" +} +[ $# != 0 ] || usage + +## $1 - X509 in PEM format +ossl_x509_verify_fmt() { + openssl x509 -in "$1" -noout >/dev/null +} + +## $1 - cert +ossl_ocsp_uri() { + openssl x509 -in "$1" -noout -ocsp_uri \ + | head -n 1 +} + +## $1 - chain +## $2 - cert +## $3 - ocsp uri +## $4 - ocsp response +ossl_ocsp_fetch() { + openssl ocsp \ + -timeout "${ocsp_fetch_timeout}" \ + -nonce \ + -issuer "$1" -cert "$2" -url "$3" -respout "$4" +} + +## $1 - ocsp response +ossl_ocsp_verify_fmt() { + openssl ocsp \ + -noverify -respin "$1" +} + +## $1 - chain +## $2 - cert +## $3 - ocsp response +ossl_ocsp_read() { + openssl ocsp \ + -issuer "$1" -cert "$2" -respin "$3" -resp_text +} + +## $1 - chain +## $2 - cert +## $3 - ocsp response +ossl_ocsp_verify() { + ossl_ocsp_read "$@" >/dev/null +} + +## stdin - output of ossl_ocsp_read() +ossl_ocsp_next_update() { + sed -En '/^\s*[Nn]ext [Uu]pdate:\s*(\S.+\S)\s*$/{s//\1/;p;q}' +} + +unset arg_ok cmd chain cert ocsp_uri ocsp_resp +arg_ok= +while : ; do + [ -n "$1" ] || break + cmd="$1" + case "$1" in + get-uri ) + [ -n "$2" ] || break + [ -s "$2" ] || break + ossl_x509_verify_fmt "$2" || break + cert="$2" + ;; + is-valid | is-expiring | fetch ) + [ -n "$2" ] || break + [ -s "$2" ] || break + ossl_x509_verify_fmt "$2" || break + chain="$2" + [ -n "$3" ] || break + [ -s "$3" ] || break + ossl_x509_verify_fmt "$3" || break + cert="$3" + [ -n "$4" ] || break + ocsp_resp="$4" + ## OCSP response validation is handled later and in various ways (!) + ## e.g. "is-valid" (cmd_is_valid) validates OCSP response as expected + ## but "is-expiring" (cmd_is_expiring) returns success for invalid OCSP response + ## which means OCSP response should be updated ASAP + ;; + *) break ;; + esac + arg_ok=1 +break ; done +[ -n "${arg_ok}" ] || usage 1 +unset arg_ok + +## OCSP URI is used only in "get-uri" and "fetch" commands +## but implicitly required for all actions +ocsp_uri=$(ossl_ocsp_uri "${cert}") || exit 1 +if [ -z "${ocsp_uri}" ] ; then + env printf '%q: unable to extract OCSP URI from %q\n' "${me}" "${cert}" >&2 + exit 1 +fi +## early command handling +if [ "${cmd}" = 'get-uri' ] ; then + printf '%s\n' "${ocsp_uri}" + exit 0 +fi + +## $1 - chain +## $2 - cert +## $3 - ocsp response +cmd_is_valid() { + ossl_ocsp_verify_fmt "$3" || return 1 + ossl_ocsp_verify "$1" "$2" "$3" || return 1 +} + +## $1 - chain +## $2 - cert +## $3 - ocsp response +cmd_is_expiring() { + cmd_is_valid "$1" "$2" "$3" || return 0 + + local need_update next ts_now ts_next ts_diff + + need_update=1 + while : ; do + next=$(ossl_ocsp_read "$1" "$2" "$3" 2>/dev/null | ossl_ocsp_next_update) + [ -n "${next}" ] || break + + ts_now=$(date '+%s') + ts_next=$(date -d "${next}" '+%s') + [ -n "${ts_next}" ] || break + [ ${ts_now} -lt ${ts_next} ] || break + + ts_diff=$((ts_next - ts_now)) + [ ${ts_diff} -le ${ocsp_valid_threshold} ] || need_update=0 + break ; done + + if [ "${need_update}" = 0 ] ; then + env printf '%q: %q has valid and fresh OCSP response\n' "${me}" "$2" >&2 + return 1 + fi + + return 0 +} + +## $1 - chain +## $2 - cert +## $3 - ocsp uri +## $4 - ocsp response +cmd_fetch() { + local t i r + + t=$(mktemp) ; : "${t:?}" + + for i in $(seq 1 "${ocsp_fetch_retries}") ; do + i= ## no-op + + if ossl_ocsp_fetch "$1" "$2" "$3" "$t" ; then + break + fi + : > "$t" + sleep "${ocsp_fetch_retry_delay}" + done + + r= + while : ; do + [ -s "$t" ] || break + cmd_is_valid "$1" "$2" "$t" || break + r=1 + break ; done + + if [ -z "$r" ] ; then + env printf '%q: unable to fetch OCSP response for %q via %q\n' "${me}" "$2" "$3" >&2 + rm -rf "$t" + return 1 + fi + + r= + while : ; do + touch "$4" || break + tee "$4" < "$t" >/dev/null || break + chmod 0644 "$4" || break + r=1 + break ; done + + if [ -z "$r" ] ; then + env printf '%q: unable to save OCSP response for %q into %q\n' "${me}" "$2" "$4" >&2 + rm -rf "$t" + return 1 + fi + + return 0 +} + +case "${cmd}" in +is-valid ) cmd_is_valid "${chain}" "${cert}" "${ocsp_resp}" ;; +is-expiring ) cmd_is_expiring "${chain}" "${cert}" "${ocsp_resp}" ;; +fetch ) cmd_fetch "${chain}" "${cert}" "${ocsp_uri}" "${ocsp_resp}" ;; +esac diff --git a/.config/dotfiles/scripts/pip-env b/.config/dotfiles/scripts/pip-env new file mode 100755 index 0000000..aeeb2e3 --- /dev/null +++ b/.config/dotfiles/scripts/pip-env @@ -0,0 +1,11 @@ +#!/bin/sh +set -ef + +set -a +PIP_DISABLE_PIP_VERSION_CHECK=1 +PIP_NO_CACHE_DIR=1 +PIP_ROOT_USER_ACTION=ignore +PIP_NO_COMPILE=1 +set +a + +exec "$@" diff --git a/.config/dotfiles/scripts/quiet b/.config/dotfiles/scripts/quiet new file mode 100755 index 0000000..f211b6a --- /dev/null +++ b/.config/dotfiles/scripts/quiet @@ -0,0 +1,38 @@ +#!/bin/sh +set +e ; set -f + +me="${0##*/}" + +usage() { + cat >&2 <<-EOF + # usage: ${me} [arguments] + EOF + exit "${1:-0}" +} +[ $# != 0 ] || usage + +t=$(mktemp) +if [ -z "$t" ] ; then + env printf "# %q: unable to create temporary file\\n" "${me}" >&2 + + ## unable to create temporary file? + ## no output in case of error + exec "$@" >/dev/null 2>/dev/null +fi + +( "$@" ; ) >"$t" 2>"$t" + +r=$? +if [ $r != 0 ] ; then + printf '# command:' + env printf ' %q' "$@" + echo + echo "# return code: $r" + if [ -s "$t" ] ; then + echo '# output:' + sed -E 's/^(.+)$/#>| \1/;s/^$/#=|/' < "$t" + fi +fi >&2 + +rm -f "$t" +exit $r diff --git a/.config/dotfiles/scripts/quilt-series-auto b/.config/dotfiles/scripts/quilt-series-auto new file mode 100755 index 0000000..e9ff00b --- /dev/null +++ b/.config/dotfiles/scripts/quilt-series-auto @@ -0,0 +1,10 @@ +#!/bin/sh +set +e ; set -f + +[ -n "${1:?}" ] || exit 1 +[ -d "$1" ] || exit 1 + +find "$1/" -follow -type f -printf '%P\0' \ +| sed -zEn '/\.(diff|patch)$/p' \ +| sort -zuV \ +| xargs -0r printf '%s\n' diff --git a/.config/dotfiles/scripts/quilt-series-strip-comments b/.config/dotfiles/scripts/quilt-series-strip-comments new file mode 100755 index 0000000..951ba9b --- /dev/null +++ b/.config/dotfiles/scripts/quilt-series-strip-comments @@ -0,0 +1,4 @@ +#!/bin/sh +set -ef + +sed -E -e '/^[[:space:]]*(#|$)/d' "$@" diff --git a/.config/dotfiles/scripts/run-as b/.config/dotfiles/scripts/run-as new file mode 100755 index 0000000..d32b37d --- /dev/null +++ b/.config/dotfiles/scripts/run-as @@ -0,0 +1,148 @@ +#!/bin/sh +set -ef + +me="${0##*/}" +usage() { + cat >&2 <<-EOF + # usage: ${me} [:] [command and arguments] + EOF + exit "${1:-0}" +} +[ $# != 0 ] || usage +[ -n "$1" ] || usage 1 + +if ! command -v setpriv >/dev/null ; then + env printf "%q: error: 'setpriv' is missing, unable to switch user!\\n" "${me}" >&2 + exit 126 +fi + +is_id() { printf '%s' "$1" | grep -Eqz '^(0|[1-9][0-9]*)$' ; } + +userspec="$1" ; shift + +unset user group _xtra +IFS=':' read -r user group _xtra <<-EOF +${userspec} +EOF + +if [ -z "${user}" ] ; then + env printf "%q: error: 'user' expected but empty\\n" "${me}" >&2 + usage 1 +fi + +if [ -n "${_xtra}" ] ; then + env printf "%q: warning: extra data in 'user:group' specifier: %q\\n" "${me}" "${_xtra}" >&2 +fi +unset _xtra + +unset _user _x _uid _gid _comment _home _shell _xtra +IFS=':' read -r _user _x _uid _gid _comment _home _shell _xtra <<-EOF +$(getent passwd "${user}" || : ) +EOF +unset _x _uid _comment _xtra + +_has_user=0 +homedir='/' +shell=/bin/sh +if [ -n "${_user}" ] ; then + _has_user=1 + user=${_user} + homedir=${_home} + shell=${_shell} +elif is_id "${user}" ; then + ## accept unknown/random uid + : +else + env printf "%q: error: 'user' is malformed - not exist nor numeric\\n" "${me}" >&2 + exit 1 +fi + +if [ -z "${group}" ] ; then + if [ ${_has_user} = 1 ] ; then + group=_gid + else + group=nogroup + fi +else + unset _group _x _gid _users _xtra + IFS=':' read -r _group _x _gid _users _xtra <<-EOF + $(getent group "${group}" || : ) + EOF + unset _x _gid _users _xtra + + if [ -n "${_group}" ] ; then + group=${_group} + elif is_id "${group}" ; then + ## accept unknown/random gid + : + else + env printf "%q: error: 'group' is malformed - not exist nor numeric\\n" "${me}" >&2 + exit 1 + fi +fi + +unset _user _gid _home _shell _group _gid _users + +# handle username and working directory +username='__non_existent_user__' +init_groups='--clear-groups' +if [ ${_has_user} = 1 ] ; then + username="${user}" + init_groups='--init-groups' +fi + +unset __exec__ +_runas() { + ${__exec__:+ exec } \ + setpriv \ + --reuid="${user}" \ + --regid="${group}" \ + ${init_groups} \ + ${inh_caps} \ + env \ + -u __exec__ \ + -C "${cwd}" \ + USER="${username}" \ + LOGNAME="${username}" \ + HOME="${homedir}" \ + SHELL="${shell}" \ + "$@" +} + +## TODO: support somehow flags for "setpriv" + +## Ubuntu 20.04 "Focal": +## setpriv: libcap-ng is too old for "all" caps +cwd=/ +inh_caps='--inh-caps=-all' +_runas true >/dev/null 2>&1 || inh_caps= + +cwd= +for i in "${PWD}" "${homedir}" / ; do + [ -d "$i" ] || continue + + cwd=/ + if _runas ls -l "$i" >/dev/null ; then + if [ "$i" != "${PWD}" ] ; then + env printf "%q: info: working directory will be changed to %q\\n" "${me}" "$i" >&2 + fi + + cwd="$i" + break + else + env printf "%q: warning: user %q can't access directory %q\\n" "${me}" "${user}" "$i" >&2 + fi + cwd= +done + +if [ -z "${cwd}" ] ; then + env printf "%q: error: unable to find appropriate working directory\\n" "${me}" >&2 + exit 1 +fi + +__exec__=1 +if [ $# = 0 ] ; then + _runas id +else + _runas "$@" +fi diff --git a/.config/dotfiles/scripts/sperl b/.config/dotfiles/scripts/sperl new file mode 120000 index 0000000..e107f63 --- /dev/null +++ b/.config/dotfiles/scripts/sperl @@ -0,0 +1 @@ +_perl-wrapper.sh \ No newline at end of file diff --git a/.config/dotfiles/scripts/static-compress b/.config/dotfiles/scripts/static-compress new file mode 100755 index 0000000..c5da175 --- /dev/null +++ b/.config/dotfiles/scripts/static-compress @@ -0,0 +1,89 @@ +#!/bin/sh +set +e ; set -f + +: "${GZIP_COMPLEVEL:=1}" +: "${BROTLI_COMPLEVEL:=1}" +: "${ZSTD_COMPLEVEL:=1}" + +has_gzip=1 has_brotli=1 has_zstd=1 +check-bin-pkg gzip || has_gzip= +check-bin-pkg brotli || has_brotli= +check-bin-pkg zstd || has_zstd= + +if [ -z "${has_gzip}${has_brotli}${has_zstd}" ] ; then + env printf "%q: suitable compressors are missing\\n" "${0##*/}" >&2 + exit 1 +fi + +do_gzip() { + [ "$1" -nt "$1.gz" ] || return 0 + gzip "-${GZIP_COMPLEVEL}kf" "$1" || return $? + comp_fixup "$1" "$1.gz" || rm -f "$1.gz" +} + +do_brotli() { + [ "$1" -nt "$1.br" ] || return 0 + brotli "-${BROTLI_COMPLEVEL}kf" "$1" || return $? + comp_fixup "$1" "$1.br" || rm -f "$1.br" +} + +do_zstd() { + [ "$1" -nt "$1.zst" ] || return 0 + zstd "-${ZSTD_COMPLEVEL}kfq" "$1" || return $? + comp_fixup "$1" "$1.zst" || rm -f "$1.zst" +} + +have_cmd() { command -v "$1" /dev/null 2>&1 ; } + +unset has_mawk +if have_cmd mawk ; then has_mawk=1 ; fi +mawk_then_awk() { + if [ -n "${has_mawk}" ] + then mawk "$@" + else awk "$@" + fi +} + +float_div() { + mawk_then_awk -v "a=$1" -v "b=$2" 'BEGIN{print a/b;exit;}' &2 <<-EOF + # usage: ${me} [] + # if is not specified then default '${_timelimit}' is used + # format: [1-9][0-9]*[smh] + EOF + exit "${1:-0}" +} + +case "$1" in +-h | --help ) usage ;; +esac + +_timeout() { timeout --verbose --kill-after=3s --foreground "$@" ; } + +tlimit="${1:-${_timelimit}}" +case "${tlimit}" in +*s | *m | *h ) + printf '%s' "${tlimit}" | grep -zEq '^[1-9][0-9]*[smh]$' || { + env printf "%q: broken format: %q\\n" "${me}" "${tlimit}" >&2 + usage 1 + } +;; +* ) + env printf "%q: unsupported format: %q\\n" "${me}" "${tlimit}" >&2 + usage 1 +;; +esac + +## verify tlimit +_timeout "${tlimit}" /bin/true || usage 1 + +_dd='dd conv=notrunc status=none' + +_timeout "${tlimit}" ${_dd} bs=1 count=1 || exit 1 +exec ${_dd} diff --git a/.config/dotfiles/scripts/ufind.sh b/.config/dotfiles/scripts/ufind.sh new file mode 100755 index 0000000..51943d0 --- /dev/null +++ b/.config/dotfiles/scripts/ufind.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set +e ; set -f + +find "$@" -follow -type f -print0 \ +| xargs -0 -r -n 128 stat -L --printf='%d:%i|%n\0' \ +| sort -z -u -t '|' -k1,1 \ +| cut -z -d '|' -f 2 \ +| tr '\0' '\n' diff --git a/.config/dotfiles/scripts/zap-tree b/.config/dotfiles/scripts/zap-tree new file mode 100644 index 0000000..2daa7d2 --- /dev/null +++ b/.config/dotfiles/scripts/zap-tree @@ -0,0 +1,30 @@ +#!/bin/sh +set +e ; set -f + +if [ -z "${ZAP_TREE}" ] ; then + export ZAP_TREE=1 + + ## intrusive parallelism + jobs=$(nproc) + jobs=$(( jobs + (jobs + 1)/2 )) + + for i ; do + [ -d "$i" ] || continue + find "$i/" -mindepth 1 -maxdepth 1 -type d -print0 + done \ + | xargs -0 -r readlink -e \ + | sort -zuV \ + | xargs -0 -r -n 1 -P "${jobs}" "$0" + + exit +fi + +find_fast() { + find "$@" -printf . -quit | grep -Fq . +} + +for i ; do + [ -d "$i" ] || continue + + find_fast "$i/" ! -type d || rm -rfv "$i" +done diff --git a/.config/zsh/alias/gpg.zsh b/.config/zsh/alias/gpg.zsh deleted file mode 100644 index e9e8f2e..0000000 --- a/.config/zsh/alias/gpg.zsh +++ /dev/null @@ -1,14 +0,0 @@ -#!/bin/zsh - -gpg-warmup() { - (( ${+commands[gpg]} )) || return 127 - - local t r - - t=$(mktemp) - command gpg -abs "$t" - r=$? - rm -f "$t" "$t.asc" - - return "$r" -} diff --git a/.config/zsh/alias/idle.zsh b/.config/zsh/alias/idle.zsh.old similarity index 100% rename from .config/zsh/alias/idle.zsh rename to .config/zsh/alias/idle.zsh.old diff --git a/.config/zsh/alias/quilt.zsh b/.config/zsh/alias/quilt.zsh deleted file mode 100644 index e36ba2a..0000000 --- a/.config/zsh/alias/quilt.zsh +++ /dev/null @@ -1,84 +0,0 @@ -#!/bin/zsh - -z-quilt() { command quilt "$@" ; } - -quilt-series-strip-comments() { - sed -E '/^[[:space:]]*(#|$)/d' "$@" -} - -quilt-series-auto() { - [ -n "${1:?}" ] - [ -d "$1" ] || return 1 - - find "$1/" -follow -type f -printf '%P\0' \ - | sed -zEn '/\.(diff|patch)$/p' \ - | sort -zuV \ - | xargs -0r printf '%s\n' -} - -krd-quilt() { - (( $+commands[quilt] )) || return 127 - - [ -n "${1:?}" ] - - local patchdir series tmp_series - - if [ -d "$1" ] ; then - patchdir="$1/debian/patches" - if [ -d "${patchdir}" ] ; then - [ -f "${patchdir}/series" ] || return 1 - else - patchdir="$1" - fi - - series="${patchdir}/series" - if ! [ -f "${series}" ] ; then - mkdir -p "$1/.pc" || return 1 - series="$1/.pc/krd-quilt-series" - touch "${series}" || return 1 - quilt-series-auto "${patchdir}" > "${series}" - fi - elif [ -f "$1" ] ; then - [ -s "$1" ] || return 1 - - series="$1" - patchdir=${series:h} - else - return 1 - fi - - local r - ( - z-quilt-default-env - set -a - QUILT_SERIES="${series}" - QUILT_PATCHES="${patchdir}" - set +a - - r=0 - while read -rs i ; do - [ -n "$i" ] || continue - - z-quilt --fuzz=0 push "$i" - r=$? ; [ $r -eq 0 ] || exit $r - z-quilt refresh "$i" - r=$? ; [ $r -eq 0 ] || exit $r - - sed -E -i \ - -e 's#^(-{3} )[^/][^/]*/(.*)$#\1a/\2#;' \ - -e 's#^(\+{3} )[^/][^/]*/(.*)$#\1b/\2#' \ - "$i" - - rm -f "$i"'~' - done <<< $( - if ! z-quilt unapplied ; then - quilt-series-strip-comments "${series}" \ - | sed -E "s${ZSHU_XSED}^${ZSHU_XSED}${patchdir}/${ZSHU_XSED}" - fi - ) - exit $r - ) - r=$? - - return $r -} diff --git a/.config/zsh/alias/sbuild.zsh b/.config/zsh/alias/sbuild.zsh deleted file mode 100644 index 72388a1..0000000 --- a/.config/zsh/alias/sbuild.zsh +++ /dev/null @@ -1,84 +0,0 @@ -#!/bin/zsh - -krd-debsrc() { - (( $+commands[deb-src-export] )) || return 127 - - [ -n "${1:?}" ] - - local dstdir - case "$1" in - */* ) dstdir="$1/src" ;; - * ) dstdir="/tmp/$1/src" ;; - esac - rm -rf "${dstdir}" - deb-src-export "${dstdir}" -} - -krd-sbuild() { - (( $+commands[sbuild] )) || return 127 - (( $+commands[xz] )) || return 127 - - [ -n "${1:?}" ] - [ -n "${2:?}" ] - - local topdir - case "$1" in - */* ) topdir="$1" ;; - * ) topdir="/tmp/$1" ;; - esac - [ -d "${topdir}" ] || return 1 - - local srcdir="${topdir}/src" - [ -d "${srcdir}" ] || return 2 - - arch="$2" - - ## done with args - shift 2 - - local -a sbuild_env sbuild_args - local i - for i ; do - ## naive splitting args and env - case "$i" in - -*) sbuild_args+=( $i ) ;; - *=* ) sbuild_env+=( $i ) ;; - *) sbuild_args+=( $i ) ;; - esac - done - - ( - for i ( ${sbuild_env} ) ; do - export "$i" - done - - z-set-tmpdir /tmp - - builddir="${topdir}/${arch}" - mkdir -p "${topdir}/all" "${builddir}" "${builddir}-all" "${builddir}-debug" - - cd "${builddir}" - for i ( "${srcdir}"/*.dsc(N.r) ) ; do - idle sbuild --arch-all --arch-any --arch=${arch} ${sbuild_args[@]} "$i" - find -name '*.build' -type l -exec rm -f {} + - find -name '*.build' -type f -exec xz -9vv {} + - done - - find \ - -name '*_all.deb' -type f \ - -exec mv -fvt "../${arch}-all" {} + - find \ - -name '*_all.ddeb' -type f \ - -exec mv -fvt "../${arch}-all" {} + - - find \ - -regextype egrep -regex '.+dbg(sym)?_[^_]+_'"${arch}"'\.d?deb$' -type f \ - -exec mv -fvt "../${arch}-debug" {} + - find \ - -name '*.ddeb' -type f \ - -exec mv -fvt "../${arch}-debug" {} + - - cd "${builddir}-all" - find -type f -exec mv -nvt '../all' {} + - ) -} diff --git a/.config/zsh/env.zsh b/.config/zsh/env.zsh index 8af8b35..c4ce5ed 100644 --- a/.config/zsh/env.zsh +++ b/.config/zsh/env.zsh @@ -14,7 +14,7 @@ z-sort-path() { ## process in-home part t=( ${(@)p:#${HOME}/*} ) - npath+=( "${ZSHU[d_scripts]}" "${ZSHU[d_bin]}" "${HOME}/bin" ) + npath+=( "${HOME}/bin" "${ZSHU[d_bin]}" "${ZSHU[d_scripts]}" ) npath+=( ${(@)p:|t} ) p=( $t )