summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
authormediocregopher <mediocregopher@gmail.com>2023-08-13 21:34:14 +0200
committermediocregopher <mediocregopher@gmail.com>2023-08-13 21:35:17 +0200
commit1957081c122fe231eb6120192489dd979d214317 (patch)
treeb8cd0812ec3c0452d54cb789ba938e6f0c0e8278 /bin
parentef8da74f1ae38d9eceb68846fe9ef402c8e30fcf (diff)
Update branch with all changes which could be brought in from private branches
For a while I was keeping a private branch where there were a lot of non-public things included, and that became the de-facto branch while this one lagged. This one is now up-to-date, all private stuff is dealt with via config files which are not committed.
Diffstat (limited to 'bin')
-rwxr-xr-xbin/dual-monitor16
-rwxr-xr-xbin/git-remote-gcrypt921
-rwxr-xr-xbin/go-playground2
-rwxr-xr-xbin/quick-reboot31
-rwxr-xr-xbin/quick-shutdown31
-rwxr-xr-xbin/rotcheck353
6 files changed, 432 insertions, 922 deletions
diff --git a/bin/dual-monitor b/bin/dual-monitor
new file mode 100755
index 0000000..9ed4d10
--- /dev/null
+++ b/bin/dual-monitor
@@ -0,0 +1,16 @@
+#!/usr/bin/env bash
+
+xrandr \
+ --output eDP-1 \
+ --primary \
+ --mode 1920x1080 \
+ --pos 0x0 \
+ --rotate normal \
+ --output DP-1 --off \
+ --output HDMI-1 --off \
+ --output DP-2 --off \
+ --output HDMI-2 \
+ --mode 1920x1080 \
+ --pos 0x0 \
+ --rotate normal \
+ --brightness 0.9
diff --git a/bin/git-remote-gcrypt b/bin/git-remote-gcrypt
deleted file mode 100755
index 8b66f2f..0000000
--- a/bin/git-remote-gcrypt
+++ /dev/null
@@ -1,921 +0,0 @@
-#!/bin/sh
-
-# git-remote-gcrypt
-#
-# Copyright (c) 2013 engla
-# Copyright (c) 2013, 2014 Joey Hess <id@joeyh.name>
-# Copyright (c) 2016 Sean Whitton <spwhitton@spwhitton.name> and contributors
-#
-# 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 3 of the License, or
-# (at your option) version 2 or 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 <http://www.gnu.org/licenses/>.
-#
-# See README.rst for usage instructions
-
-set -e # errexit
-set -f # noglob
-set -C # noclobber
-
-export GITCEPTION="${GITCEPTION:-}+" # Reuse $Gref except when stacked
-Gref="refs/gcrypt/gitception$GITCEPTION"
-Gref_rbranch="refs/heads/master"
-Packkey_bytes=63 # nbr random bytes for packfile keys, any >= 256 bit is ok
-Hashtype=SHA256 # SHA512 SHA384 SHA256 SHA224 supported.
-Manifestfile=91bd0c092128cf2e60e1a608c31e92caf1f9c1595f83f2890ef17c0e4881aa0a
-Hex40="[a-f0-9]"
-Hex40=$Hex40$Hex40$Hex40$Hex40$Hex40$Hex40$Hex40$Hex40
-Hex40=$Hex40$Hex40$Hex40$Hex40$Hex40 # Match SHA-1 hexdigest
-GPG="$(git config --get "gpg.program" '.+' || echo gpg)"
-
-Did_find_repo= # yes for connected, no for no repo
-Localdir="${GIT_DIR:=.git}/remote-gcrypt"
-Tempdir=
-
-Repoid=
-Refslist=
-Packlist=
-Keeplist=
-Extnlist=
-Repack_limit=25
-
-Recipients=
-
-# compat/utility functions
-# xfeed: The most basic output function puts $1 into the stdin of $2..$#
-xfeed()
-{
- local input_=
- input_=$1; shift
- "$@" <<EOF
-$input_
-EOF
-}
-xecho() { xfeed "$*" cat; }
-xecho_n() { xecho "$@" | tr -d \\n ; } # kill newlines
-echo_git() { xecho "$@" ; } # Code clarity
-echo_info() { xecho "gcrypt:" "$@" >&2; }
-echo_die() { echo_info "$@" ; exit 1; }
-
-isnull() { case "$1" in "") return 0;; *) return 1;; esac; }
-isnonnull() { ! isnull "$1"; }
-iseq() { case "$1" in "$2") return 0;; *) return 1;; esac; }
-isnoteq() { ! iseq "$1" "$2"; }
-negate() { ! "$@"; }
-
-# Execute $@ or die
-pipefail()
-{
- "$@" || { echo_info "'$1' failed!"; kill $$; exit 1; }
-}
-
-isurl() { isnull "${2%%$1://*}"; }
-islocalrepo() { isnull "${1##/*}" && [ ! -e "$1/HEAD" ]; }
-
-xgrep() { command grep "$@" || : ; }
-
-# setvar is used for named return variables
-# $1 *must* be a valid variable name, $2 is any value
-#
-# Conventions
-# return variable names are passed with a @ prefix
-# return variable functions use f_ prefix local vars
-# return var consumers use r_ prefix vars (or Titlecase globals)
-setvar()
-{
- isnull "${1##@*}" || echo_die "Missing @ for return variable: $1"
- eval ${1#@}=\$2
-}
-
-Newline="
-"
-
-# $1 is return var, $2 is value appended with newline separator
-append_to()
-{
- local f_append_tmp_=
- eval f_append_tmp_=\$${1#@}
- isnull "$f_append_tmp_" || f_append_tmp_=$f_append_tmp_$Newline
- setvar "$1" "$f_append_tmp_$2"
-}
-
-# Pick words from each line
-# $1 return variable name
-# $2 input value
-pick_fields_1_2()
-{
- local f_ret= f_one= f_two=
- while read f_one f_two _ # from << here-document
- do
- f_ret="$f_ret$f_one $f_two$Newline"
- done <<EOF
-$2
-EOF
- setvar "$1" "${f_ret#$Newline}"
-}
-
-# Take all lines matching $2 (full line)
-# $1 return variable name
-# $2 filter word
-# $3 input value
-# if $1 is a literal `!', the match is reversed (and arguments shift)
-# we instead remove all lines matching
-filter_to()
-{
- local f_neg= f_line= f_ret= IFS=
- isnoteq "$1" "!" || { f_neg=negate; shift; }
- IFS=$Newline
- for f_line in $3
- do
- $f_neg isnonnull "${f_line##$2}" || f_ret=$f_ret$f_line$Newline
- done
- setvar "$1" "${f_ret%$Newline}"
-}
-
-# Output the number of lines in $1
-line_count()
-{
- local IFS=
- IFS=$Newline
- set -- $1
- xecho "$#"
-}
-
-
-## gitception part
-# Fetch giturl $1, file $2
-gitception_get()
-{
- # Take care to preserve FETCH_HEAD
- local ret_=: obj_id= fet_head="$GIT_DIR/FETCH_HEAD"
- [ -e "$fet_head" ] && command mv -f "$fet_head" "$fet_head.$$~" || :
- git fetch -q -f "$1" "$Gref_rbranch:$Gref" >/dev/null &&
- obj_id="$(git ls-tree "$Gref" | xgrep -E '\b'"$2"'$' | awk '{print $3}')" &&
- isnonnull "$obj_id" && git cat-file blob "$obj_id" && ret_=: ||
- { ret_=false && : ; }
- [ -e "$fet_head.$$~" ] && command mv -f "$fet_head.$$~" "$fet_head" || :
- $ret_
-}
-
-anon_commit()
-{
- GIT_AUTHOR_NAME="root" GIT_AUTHOR_EMAIL="root@localhost" \
- GIT_AUTHOR_DATE="1356994801 -0400" GIT_COMMITTER_NAME="root" \
- GIT_COMMITTER_EMAIL="root@localhost" \
- GIT_COMMITTER_DATE="1356994801 -0400" \
- git commit-tree "$@" <<EOF
-Initial commit
-EOF
-}
-
-# Get 'tree' from $1, change file $2 to obj id $3
-update_tree()
-{
- local tab_=" "
- # $2 is a filename from the repo format
- (set +e;
- git ls-tree "$1" | xgrep -v -E '\b'"$2"'$';
- xecho "100644 blob $3$tab_$2"
- ) | git mktree
-}
-
-# Put giturl $1, file $2
-# depends on previous GET to set $Gref and depends on PUT_FINAL later
-gitception_put()
-{
- local obj_id= tree_id= commit_id=
- obj_id=$(git hash-object -w --stdin) &&
- tree_id=$(update_tree "$Gref" "$2" "$obj_id") &&
- commit_id=$(anon_commit "$tree_id") &&
- git update-ref "$Gref" "$commit_id"
-}
-
-# Remove giturl $1, file $2
-# depends on previous GET like put
-gitception_remove()
-{
- local tree_id= commit_id= tab_=" "
- # $2 is a filename from the repo format
- tree_id=$(git ls-tree "$Gref" | xgrep -v -E '\b'"$2"'$' | git mktree) &&
- commit_id=$(anon_commit "$tree_id") &&
- git update-ref "$Gref" "$commit_id"
-}
-
-gitception_new_repo()
-{
- local commit_id= empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904
- # get any file to update Gref, and if it's not updated we create empty
- git update-ref -d "$Gref" || :
- gitception_get "$1" "x" 2>/dev/null >&2 || :
- git rev-parse -q --verify "$Gref" >/dev/null && return 0 ||
- commit_id=$(anon_commit "$empty_tree") &&
- git update-ref "$Gref" "$commit_id"
-}
-## end gitception
-
-# Fetch repo $1, file $2, tmpfile in $3
-GET()
-{
- if isurl sftp "$1"
- then
- (exec 0>&-; curl -s -S -k "$1/$2") > "$3"
- elif isurl rsync "$1"
- then
- (exec 0>&-; rsync -I -W "${1#rsync://}"/"$2" "$3" >&2)
- elif islocalrepo "$1"
- then
- cat "$1/$2" > "$3"
- else
- gitception_get "${1#gitception://}" "$2" > "$3"
- fi
-}
-
-# Put repo $1, file $2 or fail, tmpfile in $3
-PUT()
-{
- if isurl sftp "$1"
- then
- curl -s -S -k --ftp-create-dirs -T "$3" "$1/$2"
- elif isurl rsync "$1"
- then
- rsync -I -W "$3" "${1#rsync://}"/"$2" >&2
- elif islocalrepo "$1"
- then
- cat >| "$1/$2" < "$3"
- else
- gitception_put "${1#gitception://}" "$2" < "$3"
- fi
-}
-
-# Put all PUT changes for repo $1 at once
-PUT_FINAL()
-{
- if isurl sftp "$1" || islocalrepo "$1" || isurl rsync "$1"
- then
- :
- else
- git push --quiet -f "${1#gitception://}" "$Gref:$Gref_rbranch"
- fi
-}
-
-# Put directory for repo $1
-PUTREPO()
-{
- if isurl sftp "$1"
- then
- :
- elif isurl rsync "$1"
- then
- rsync -q -r --exclude='*' "$Localdir/" "${1#rsync://}" >&2
- elif islocalrepo "$1"
- then
- mkdir -p "$1"
- else
- gitception_new_repo "${1#gitception://}"
- fi
-}
-
-# For repo $1, delete all newline-separated files in $2
-REMOVE()
-{
- local fn_=
- if isurl sftp "$1"
- then
- # FIXME
- echo_info "sftp: Ignore remove request $1/$2"
- elif isurl rsync "$1"
- then
- xfeed "$2" rsync -I -W -v -r --delete --include-from=- \
- --exclude='*' "$Localdir"/ "${1#rsync://}/" >&2
- elif islocalrepo "$1"
- then
- for fn_ in $2; do
- rm -f "$1"/"$fn_"
- done
- else
- for fn_ in $2; do
- gitception_remove "${1#gitception://}" "$fn_"
- done
- fi
-}
-
-CLEAN_FINAL()
-{
- if isurl sftp "$1" || islocalrepo "$1" || isurl rsync "$1"
- then
- :
- else
- git update-ref -d "$Gref" || :
- fi
-}
-
-ENCRYPT()
-{
- rungpg --batch --force-mdc --compress-algo none --trust-model=always --passphrase-fd 3 -c 3<<EOF
-$1
-EOF
-}
-
-DECRYPT()
-{
- rungpg -q --batch --no-default-keyring --secret-keyring /dev/null \
- --keyring /dev/null --passphrase-fd 3 -d 3<<EOF
-$1
-EOF
-}
-
-# Encrypt to recipients $1
-PRIVENCRYPT()
-{
- set -- $1
- if isnonnull "$Conf_signkey"; then
- set -- "$@" -u "$Conf_signkey"
- fi
- rungpg --compress-algo none --trust-model=always -se "$@"
-}
-
-# $1 is the match for good signature, $2 is the textual signers list
-PRIVDECRYPT()
-{
- local status_=
- exec 4>&1 &&
- status_=$(rungpg --status-fd 3 -q -d 3>&1 1>&4) &&
- xfeed "$status_" grep "^\[GNUPG:\] ENC_TO " >/dev/null &&
- (xfeed "$status_" grep -e "$1" >/dev/null || {
- echo_info "Failed to verify manifest signature!" &&
- echo_info "Only accepting signatories: ${2:-(none)}" &&
- return 1
- })
-}
-
-# Generate $1 random bytes
-genkey()
-{
- rungpg --armor --gen-rand 1 "$1"
-}
-
-gpg_hash()
-{
- local hash_=
- hash_=$(rungpg --with-colons --print-md "$1" | tr A-F a-f)
- hash_=${hash_#:*:}
- xecho "${hash_%:}"
-}
-
-rungpg()
-{
- if isnonnull "$Conf_gpg_args"; then
- set -- "$Conf_gpg_args" "$@"
- fi
- # gpg will fail to run when there is no controlling tty,
- # due to trying to print messages to it, even if a gpg agent is set
- # up. --no-tty fixes this.
- if [ "x$GPG_AGENT_INFO" != "x" ]; then
- ${GPG} --no-tty $@
- else
- ${GPG} $@
- fi
-}
-
-# Pass the branch/ref by pipe to git
-safe_git_rev_parse()
-{
- git cat-file --batch-check 2>/dev/null |
- xgrep -v "missing" | cut -f 1 -d ' '
-}
-
-make_new_repo()
-{
- echo_info "Setting up new repository"
- PUTREPO "$URL"
-
- # Needed assumption: the same user should have no duplicate Repoid
- Repoid=":id:$(genkey 15)"
- iseq "${NAME#gcrypt::}" "$URL" ||
- git config "remote.$NAME.gcrypt-id" "$Repoid"
- echo_info "Remote ID is $Repoid"
- Extnlist="extn comment"
-}
-
-
-# $1 return var for goodsig match, $2 return var for signers text
-read_config()
-{
- local recp_= r_tail= r_keyinfo= r_keyfpr= gpg_list= cap_= conf_part= good_sig= signers_=
- Conf_signkey=$(git config --get "remote.$NAME.gcrypt-signingkey" '.+' ||
- git config --path user.signingkey || :)
- conf_part=$(git config --get "remote.$NAME.gcrypt-participants" '.+' ||
- git config --get gcrypt.participants '.+' || :)
- Conf_pubish_participants=$(git config --get --bool "remote.$NAME.gcrypt-publish-participants" '.+' ||
- git config --get --bool gcrypt.publish-participants || :)
- Conf_gpg_args=$(git config --get gcrypt.gpg-args '.+' || :)
-
- # Figure out which keys we should encrypt to or accept signatures from
- if isnull "$conf_part" || iseq "$conf_part" simple
- then
- signers_="(default keyring)"
- Recipients="--throw-keyids --default-recipient-self"
- good_sig="^\[GNUPG:\] GOODSIG "
- setvar "$1" "$good_sig"
- setvar "$2" "$signers_"
- return 0
- fi
-
- for recp_ in $conf_part
- do
- gpg_list=$(rungpg --with-colons --fingerprint -k "$recp_")
- r_tail_=$(echo "$recp_" | sed -e 's/^0x//')
- filter_to @r_keyinfo "pub*" "$gpg_list"
- if echo "$recp_" | grep -E -q '^[xA-F0-9]+$'; then # is $recp_ a keyid?
- filter_to @r_keyfpr "fpr*$r_tail_*" "$gpg_list"
- else
- filter_to @r_keyfpr "fpr*" "$gpg_list"
- fi
- isnull "$r_keyinfo" || isnonnull "${r_keyinfo##*"$Newline"*}" ||
- echo_info "WARNING: '$recp_' matches multiple keys, using one"
- isnull "$r_keyfpr" || isnonnull "${r_keyfpr##*"$Newline"*}" ||
- echo_info "WARNING: '$recp_' matches multiple fingerprints, using one"
- r_keyinfo=${r_keyinfo%%"$Newline"*}
- r_keyfpr=${r_keyfpr%%"$Newline"*}
- keyid_=$(xfeed "$r_keyinfo" cut -f 5 -d :)
- fprid_=$(xfeed "$r_keyfpr" cut -f 10 -d :)
-
- isnonnull "$fprid_" &&
- signers_="$signers_ $keyid_" &&
- append_to @good_sig "^\[GNUPG:\] VALIDSIG .*$fprid_$" || {
- echo_info "WARNING: Skipping missing key $recp_"
- continue
- }
- # Check 'E'ncrypt capability
- cap_=$(xfeed "$r_keyinfo" cut -f 12 -d :)
- if ! iseq "${cap_#*E}" "$cap_"; then
- if [ "$Conf_pubish_participants" = true ]; then
- Recipients="$Recipients -r $keyid_"
- else
- Recipients="$Recipients -R $keyid_"
- fi
- fi
- done
-
- if isnull "$Recipients"
- then
- echo_info "You have not configured any keys you can encrypt to" \
- "for this repository"
- echo_info "Use ::"
- echo_info " git config gcrypt.participants YOURKEYID"
- exit 1
- fi
- setvar "$1" "$good_sig"
- setvar "$2" "$signers_"
-}
-
-ensure_connected()
-{
- local manifest_= r_repoid= r_name= url_frag= r_sigmatch= r_signers= \
- tmp_manifest=
-
- if isnonnull "$Did_find_repo"
- then
- return
- fi
- Did_find_repo=no
- read_config @r_sigmatch @r_signers
-
- iseq "${NAME#gcrypt::}" "$URL" || r_name=$NAME
-
- if isurl gitception "$URL" && isnonnull "$r_name"; then
- git config "remote.$r_name.url" "gcrypt::${URL#gitception://}"
- echo_info "Updated URL for $r_name, gitception:// -> ()"
- fi
-
- # Find the URL fragment
- url_frag=${URL##*"#"}
- isnoteq "$url_frag" "$URL" || url_frag=
- URL=${URL%"#$url_frag"}
-
- # manifestfile -- sha224 hash if we can, else the default location
- if isurl sftp "$URL" || islocalrepo "$URL" || isurl rsync "$URL"
- then
- # not for gitception
- isnull "$url_frag" ||
- Manifestfile=$(xecho_n "$url_frag" | gpg_hash SHA224)
- else
- isnull "$url_frag" || Gref_rbranch="refs/heads/$url_frag"
- fi
-
- Repoid=
- isnull "$r_name" ||
- Repoid=$(git config "remote.$r_name.gcrypt-id" || :)
-
-
- tmp_manifest="$Tempdir/maniF"
- GET "$URL" "$Manifestfile" "$tmp_manifest" 2>/dev/null || {
- echo_info "Repository not found: $URL"
- if ! isnull "$Repoid"; then
- echo_info "..but repository ID is set. Aborting."
- return 1
- else
- return 0
- fi
- }
-
- Did_find_repo=yes
- echo_info "Decrypting manifest"
- manifest_=$(PRIVDECRYPT "$r_sigmatch" "$r_signers" < "$tmp_manifest") &&
- isnonnull "$manifest_" ||
- echo_die "Failed to decrypt manifest!"
- rm -f "$tmp_manifest"
-
- filter_to @Refslist "$Hex40 *" "$manifest_"
- filter_to @Packlist "pack :*:* *" "$manifest_"
- filter_to @Keeplist "keep :*:*" "$manifest_"
- filter_to @Extnlist "extn *" "$manifest_"
- filter_to @r_repoid "repo *" "$manifest_"
-
- r_repoid=${r_repoid#repo }
- r_repoid=${r_repoid% *}
- if isnull "$Repoid"
- then
- echo_info "Remote ID is $r_repoid"
- Repoid=$r_repoid
- elif isnoteq "$r_repoid" "$Repoid"
- then
- echo_info "WARNING:"
- echo_info "WARNING: Remote ID has changed!"
- echo_info "WARNING: from $Repoid"
- echo_info "WARNING: to $r_repoid"
- echo_info "WARNING:"
- Repoid=$r_repoid
- else
- return 0
- fi
-
- isnull "$r_name" || git config "remote.$r_name.gcrypt-id" "$r_repoid"
-}
-
-# $1 is the hash type (SHA256 etc)
-# $2 the pack id
-# $3 the key
-get_verify_decrypt_pack()
-{
- local rcv_id= tmp_encrypted=
- tmp_encrypted="$Tempdir/packF"
- GET "$URL" "$2" "$tmp_encrypted" &&
- rcv_id=$(gpg_hash "$1" < "$tmp_encrypted") &&
- iseq "$rcv_id" "$2" || echo_die "Packfile $2 does not match digest!"
- DECRYPT "$3" < "$tmp_encrypted"
- rm -f "$tmp_encrypted"
-}
-
-# download all packlines (pack :SHA256:a32abc1231) from stdin (or die)
-# $1 destdir (when repack, else "")
-get_pack_files()
-{
- local pack_id= r_pack_key_line= htype_= pack_= key_=
- while IFS=': ' read -r _ htype_ pack_ # <<here-document
- do
- isnonnull "$pack_" || continue
-
- # Get the Packlist line with the key
- pack_id=":${htype_}:$pack_"
- filter_to @r_pack_key_line "pack $pack_id *" "$Packlist"
- key_=${r_pack_key_line#pack $pack_id }
-
- if isnonnull "${pack_##$Hex40*}" ||
- isnoteq "$htype_" SHA256 && isnoteq "$htype_" SHA224 &&
- isnoteq "$htype_" SHA384 && isnoteq "$htype_" SHA512
- then
- echo_die "Packline malformed: $pack_id"
- fi
-
- get_verify_decrypt_pack "$htype_" "$pack_" "$key_" | \
- if isnull "${1:-}"
- then
- # add to local pack list
- git index-pack -v --stdin >/dev/null
- xecho "pack $pack_id" >> "$Localdir/have_packs$GITCEPTION"
- else
- git index-pack -v --stdin "$1/${pack_}.pack" >/dev/null
- fi
- done
-}
-
-# Download and unpack remote packfiles
-# $1 return var for list of packfiles to delete
-repack_if_needed()
-{
- local n_= m_= kline_= r_line= r_keep_packlist= r_del_list=
-
- isnonnull "$Packlist" || return 0
-
- if isnonnull "${GCRYPT_FULL_REPACK:-}"
- then
- Keeplist=
- Repack_limit=0
- fi
-
- pick_fields_1_2 @r_del_list "$Packlist"
-
- n_=$(line_count "$Packlist")
- m_=$(line_count "$Keeplist")
- if iseq 0 "$(( $Repack_limit < ($n_ - $m_) ))"; then
- return
- fi
- echo_info "Repacking remote $NAME, ..."
-
- mkdir "$Tempdir/pack"
-
- # Split packages to keep and to repack
- if isnonnull "$Keeplist"; then
- while read -r _ kline_ _ # <<here-document
- do
- isnonnull "$kline_" || continue
- filter_to @r_line "pack $kline_ *" "$Packlist"
- append_to @r_keep_packlist "$r_line"
- filter_to ! @r_del_list "pack $kline_" "$r_del_list"
- done <<EOF
-$Keeplist
-EOF
- fi
-
- xfeed "$r_del_list" get_pack_files "$Tempdir/pack/"
-
- (set +f; pipefail git verify-pack -v "$Tempdir"/pack/*.idx) |
- grep -E '^[0-9a-f]{40}' | cut -f 1 -d ' '
-
- Packlist=$r_keep_packlist
- setvar "$1" "$r_del_list"
-}
-
-do_capabilities()
-{
- echo_git fetch
- echo_git push
- echo_git
-}
-
-do_list()
-{
- local obj_id= ref_name= line_=
- ensure_connected
-
- xecho "$Refslist" | while read line_
- do
- isnonnull "$line_" || break
- obj_id=${line_%% *}
- ref_name=${line_##* }
- echo_git "$obj_id" "$ref_name"
- if iseq "$ref_name" "refs/heads/master"
- then
- echo_git "@refs/heads/master HEAD"
- fi
- done
-
- # end with blank line
- echo_git
-}
-
-do_fetch()
-{
- # Download packs in the manifest that don't appear in have_packs
- local pneed_= premote_=
-
- ensure_connected
-
- # The `+` for $GITCEPTION is pointless but we will be safe for stacking
- pick_fields_1_2 @premote_ "$Packlist"
- if [ -s "$Localdir/have_packs+" ]
- then
- pneed_=$(xfeed "$premote_" xgrep -v -x -f "$Localdir/have_packs+")
- else
- pneed_=$premote_
- fi
-
- xfeed "$pneed_" get_pack_files
-
- echo_git # end with blank line
-}
-
-# do_push PUSHARGS (multiple lines like +src:dst, with both + and src opt.)
-do_push()
-{
- # Security protocol:
- # Each git packfile is encrypted and then named for the encrypted
- # file's hash. The manifest is updated with the pack id.
- # The manifest is encrypted.
- local r_revlist= pack_id= key_= obj_= src_= dst_= \
- r_pack_delete= tmp_encrypted= tmp_objlist= tmp_manifest=
-
- ensure_connected
-
- if iseq "$Did_find_repo" "no"
- then
- make_new_repo
- fi
-
- if isnonnull "$Refslist"
- then
- # mark all remote refs with ^<sha-1> (if sha-1 exists locally)
- r_revlist=$(xfeed "$Refslist" cut -f 1 -d ' ' |
- safe_git_rev_parse | sed -e 's/^\(.\)/^&/')
- fi
-
- while IFS=: read -r src_ dst_ # << +src:dst
- do
- src_=${src_#+}
- filter_to ! @Refslist "$Hex40 $dst_" "$Refslist"
-
- if isnonnull "$src_"
- then
- append_to @r_revlist "$src_"
- obj_=$(xfeed "$src_" safe_git_rev_parse)
- append_to @Refslist "$obj_ $dst_"
- fi
- done <<EOF
-$1
-EOF
-
- tmp_encrypted="$Tempdir/packP"
- tmp_objlist="$Tempdir/objlP"
-
- {
- xfeed "$r_revlist" git rev-list --objects --stdin --
- repack_if_needed @r_pack_delete
- } > "$tmp_objlist"
-
- # Only send pack if we have any objects to send
- if [ -s "$tmp_objlist" ]
- then
- key_=$(genkey "$Packkey_bytes")
- pack_id=$(export GIT_ALTERNATE_OBJECT_DIRECTORIES=$Tempdir;
- pipefail git pack-objects --stdout < "$tmp_objlist" |
- pipefail ENCRYPT "$key_" |
- tee "$tmp_encrypted" | gpg_hash "$Hashtype")
-
- append_to @Packlist "pack :${Hashtype}:$pack_id $key_"
- if isnonnull "$r_pack_delete"
- then
- append_to @Keeplist "keep :${Hashtype}:$pack_id 1"
- fi
- fi
-
- # Generate manifest
- echo_info "Encrypting to: $Recipients"
- echo_info "Requesting manifest signature"
-
- tmp_manifest="$Tempdir/maniP"
- PRIVENCRYPT "$Recipients" > "$tmp_manifest" <<EOF
-$Refslist
-$Packlist
-$Keeplist
-repo $Repoid
-$Extnlist
-EOF
-
- # Upload pack
- if [ -s "$tmp_objlist" ]
- then
- PUT "$URL" "$pack_id" "$tmp_encrypted"
- fi
-
- # Upload manifest
- PUT "$URL" "$Manifestfile" "$tmp_manifest"
-
- rm -f "$tmp_encrypted"
- rm -f "$tmp_objlist"
- rm -f "$tmp_manifest"
-
- # Delete packs
- if isnonnull "$r_pack_delete"; then
- REMOVE "$URL" "$(xecho "$r_pack_delete" | \
- while IFS=': ' read -r _ _ pack_
- do
- isnonnull "$pack_" || continue
- xecho "$pack_"
- done)"
- fi
-
- PUT_FINAL "$URL"
-
- # ok all updates
- while IFS=: read -r src_ dst_ # << +src:dst
- do
- echo_git "ok $dst_"
- done <<EOF
-$1
-EOF
-
- echo_git
-}
-
-cleanup_tmpfiles()
-{
- if isnonnull "${Tempdir%%*."$$"}"; then
- echo_die "Unexpected Tempdir value: $Tempdir"
- fi
- rm -r -f -- "${Tempdir}" >&2
-}
-
-setup()
-{
- mkdir -p "$Localdir"
-
- # Set up a subdirectory in /tmp
- temp_key=$(genkey 9 | tr '/' _)
- Tempdir="${TMPDIR:-/tmp}/git-remote-gcrypt-${temp_key}.$$"
- case "${MSYSTEM:-unknown}" in
- MSYS*|MINGW*)
- mkdir "${Tempdir}"
- echo_info "Warning: Not securing tempdir ${Tempdir} because we are on mingw/msys"
- ;;
- unknown|*)
- mkdir -m 700 "${Tempdir}"
- ;;
- esac
-
- trap cleanup_tmpfiles EXIT
- trap 'exit 1' 1 2 3 15
-}
-
-# handle git-remote-helpers protocol
-gcrypt_main_loop()
-{
- local input_= input_inner= r_args= temp_key=
-
- NAME=$1 # Remote name
- URL=$2 # Remote URL
-
- setup
-
- while read input_
- do
- case "$input_" in
- capabilities)
- do_capabilities
- ;;
- list|list\ for-push)
- do_list
- ;;
- fetch\ *)
- r_args=${input_##fetch }
- while read input_inner
- do
- case "$input_inner" in
- fetch*)
- r_args= #ignored
- ;;
- *)
- break
- ;;
- esac
- done
- do_fetch "$r_args"
- ;;
- push\ *)
- r_args=${input_##push }
- while read input_inner
- do
- case "$input_inner" in
- push\ *)
- append_to @r_args "${input_inner#push }"
- ;;
- *)
- break
- ;;
- esac
- done
- do_push "$r_args"
- ;;
- ?*)
- echo_die "Unknown input!"
- ;;
- *)
- CLEAN_FINAL "$URL"
- exit 0
- ;;
- esac
- done
-}
-
-if [ "x$1" = x--check ]
-then
- NAME=dummy-gcrypt-check
- URL=$2
- setup
- ensure_connected
- git remote remove $NAME 2>/dev/null || true
- if iseq "$Did_find_repo" "no"
- then
- exit 100
- fi
-else
- gcrypt_main_loop "$@"
-fi
diff --git a/bin/go-playground b/bin/go-playground
index 37675d0..64633a9 100755
--- a/bin/go-playground
+++ b/bin/go-playground
@@ -1,5 +1,5 @@
#!/bin/sh
cd "$(mktemp -d)";
go mod init local-playground;
-echo -e 'package main\n\nimport (\n\t"fmt"\n)\n\nfunc main() {\n\tfmt.Println("aloha")\n}\n' > main.go;
+echo 'package main\n\nimport (\n\t"fmt"\n)\n\nfunc main() {\n\tfmt.Println("aloha")\n}\n' > main.go;
$EDITOR main.go;
diff --git a/bin/quick-reboot b/bin/quick-reboot
new file mode 100755
index 0000000..9f7b751
--- /dev/null
+++ b/bin/quick-reboot
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+set -e
+
+# This assumes that /proc/cmdline contains a cryptdevice with a UUID identifier,
+# like:
+#
+# cryptdevice=UUID=1ff1d6f7-7540-4500-8011-1abe1e9ac00d:cryptroot
+uuid=$(cat /proc/cmdline | \
+ tr ' ' '\n' | \
+ grep cryptdevice | \
+ cut -d= -f3 | \
+ cut -d: -f1)
+
+device=$(lsblk -o PATH,UUID | grep "$uuid" | awk '{print $1}')
+echo "Root device is $device"
+
+echo -n "Enter root key: "
+read -s pw
+echo ""
+
+# This will check if the key is right, and cause the process to exit if not due
+# to the "set -e"
+echo "Checking key..."
+echo "$pw" | sudo cryptsetup open --test-passphrase "$device"
+
+echo "Good job, writing /boot/keyfile..."
+echo -n "$pw" | sudo tee /boot/keyfile >/dev/null
+
+echo "Rebooting..."
+sudo systemctl reboot
diff --git a/bin/quick-shutdown b/bin/quick-shutdown
new file mode 100755
index 0000000..f5a5259
--- /dev/null
+++ b/bin/quick-shutdown
@@ -0,0 +1,31 @@
+#!/bin/sh
+
+set -e
+
+# This assumes that /proc/cmdline contains a cryptdevice with a UUID identifier,
+# like:
+#
+# cryptdevice=UUID=1ff1d6f7-7540-4500-8011-1abe1e9ac00d:cryptroot
+uuid=$(cat /proc/cmdline | \
+ tr ' ' '\n' | \
+ grep cryptdevice | \
+ cut -d= -f3 | \
+ cut -d: -f1)
+
+device=$(lsblk -o PATH,UUID | grep "$uuid" | awk '{print $1}')
+echo "Root device is $device"
+
+echo -n "Enter root key: "
+read -s pw
+echo ""
+
+# This will check if the key is right, and cause the process to exit if not due
+# to the "set -e"
+echo "Checking key..."
+echo "$pw" | sudo cryptsetup open --test-passphrase "$device"
+
+echo "Good job, writing /boot/keyfile..."
+echo -n "$pw" | sudo tee /boot/keyfile >/dev/null
+
+echo "Shutting down..."
+sudo systemctl poweroff
diff --git a/bin/rotcheck b/bin/rotcheck
new file mode 100755
index 0000000..c8a59fe
--- /dev/null
+++ b/bin/rotcheck
@@ -0,0 +1,353 @@
+#!/bin/sh
+set -uf
+IFS="$(printf '\n\t')"
+LC_ALL="C"
+
+# Copyright (C) 2019 Jamie Nguyen <j@jamielinux.com>
+#
+# A simple shell script to recursively generate, update and verify checksums
+# for files you care about. It's useful for detecting bit rot.
+#
+# It's written in POSIX shell, but requires GNU coreutils, BusyBox or some
+# other collection that includes similar checksum tools.
+
+VERSION=1.1.2
+COMMAND="sha512sum"
+CHECKFILE="./.rotcheck"
+
+APPEND_MODE=0
+CHECK_MODE=0
+DELETE_MODE=0
+UPDATE_MODE=0
+
+IGNORE_MISSING=0
+FOLLOW_SYMLINKS=1
+VERBOSE=0
+WARN_FORMATTING=0
+EXCLUDE_HIDDEN=0
+FORCE_UPDATE=0
+
+usage() {
+ cat << EOF
+rotcheck $VERSION
+Usage: rotcheck MODE [OPTIONS]
+ or: rotcheck MODE [OPTIONS] -- [DIRECTORY]... [ARBITRARY FIND OPTION]...
+Recursively generate, update and verify checksums.
+
+MODES:
+ -a APPEND mode: Record checksums for any files without a checksum
+ already. Never modify existing checksums.
+ -c CHECK mode: Check that files checksums are the same.
+ -d DELETE mode: Remove checksums for files that don't exist.
+ -u APPEND-AND-UPDATE mode: Like append-only mode, but also update
+ checksums for files with a modification date newer than the
+ the checksum file. (NB: Also see \`-M\`.)
+
+OPTIONS:
+ -b COMMAND Checksum command to use. Default: sha512sum
+ -f FILE File to store checksums. For relative paths, prefix with "./"
+ or the checksum file will be checksummed. Default: ./.rotcheck
+ -h Display this help.
+ -n Don't follow symlinks. The default is to follow symlinks.
+ -v Be more verbose when adding, deleting, changing or verifying
+ checksums.
+ -w Warn about improperly formatted checksum lines.
+ -x Exclude all hidden files and directories when generating
+ checksums. The default is to include them.
+ -M Use with \`-u\` to update checksums regardless of modification
+ time. This is very slow so avoid if possible; try \`touch\`
+ instead to bump the modification time of specific files.
+ WARNING: The checksums might have changed due to bit rot so
+ use this option with care!
+
+ (specific to GNU coreutils >= 8.25)
+ -i Ignore missing files when verifying checksums.
+
+
+Supported commands:
+ GNU coreutils:
+ md5sum, sha1sum, sha224sum, sha256sum, sha384sum, sha512sum, b2sum
+
+ BusyBox (applets must be symlinked):
+ md5sum, sha1sum, sha256sum, sha512sum, sha3sum
+
+ BSD & macOS (install GNU coreutils):
+ gmd5sum, gsha1sum, gsha224sum, gsha256sum, gsha384sum, gsha512sum, gb2sum
+
+
+Examples:
+ # Create checksum file (located at "./.rotcheck"):
+ rotcheck -a
+
+ # You've added some new files and need to append some checksums:
+ rotcheck -va
+
+ # You've edited some files and need to update the checksums (for files with
+ # a modification time newer than the checksum file):
+ rotcheck -vu
+
+ # Verify checksums:
+ rotcheck -c
+
+ # Search other directories instead of the current directory.
+ # WARNING: checksums might get duplicated if mixing relative and absolute
+ # paths, or if you change the way you specify directory paths!
+ rotcheck -a -- /mnt/archive-2018/ /mnt/archive-2019/
+
+ # Exclude .git folders (these arguments are passed directly to find):
+ rotcheck -a -- ! -path '*/\\.git/*'
+
+EOF
+ exit 0
+}
+
+fail() {
+ printf '%s\n' "$@"; exit 1
+}
+
+# Curiously, I stumbled across a bug in bash-3.0.16 (c. 2004) or older
+# where \0177 (DEL) isn't handled properly. See the `find_safe` function below.
+# bash-3.1 (c. 2005), dash-0.5.2 (c. 2005), and zsh-3.1 (c. 2000) all work
+# and probably others too.
+if [ -n ${BASH+x} ] && [ -n ${BASH_VERSION+x} ]; then
+ if printf '%s' "${BASH_VERSION:-x}" | grep -qE '^[0-2]+|^3\.0'; then
+ fail "bash-3.0.16 and older are broken." \
+ "Try bash>=3.1, dash, zsh, or another POSIX shell."
+ fi
+fi
+
+# Command-line arguments. `getopts` is POSIX, while `getopt` is not.
+[ $# -gt 0 ] && [ "$1" = "--help" ] && usage
+while getopts ":acdub:f:hinvwxM" opt; do
+ case "$opt" in
+ a) APPEND_MODE=1;;
+ c) CHECK_MODE=1;;
+ d) DELETE_MODE=1;;
+ u) UPDATE_MODE=1;;
+ b) COMMAND="$OPTARG";;
+ f) CHECKFILE="$OPTARG";;
+ h) usage;;
+ i) IGNORE_MISSING=1;;
+ n) FOLLOW_SYMLINKS=0;;
+ v) VERBOSE=1;;
+ w) WARN_FORMATTING=1;;
+ x) EXCLUDE_HIDDEN=1;;
+ M) FORCE_UPDATE=1;;
+ \?) fail "-$OPTARG: Invalid argument";;
+ :) fail "-$OPTARG requires an argument";;
+ esac
+done; shift $(($OPTIND - 1))
+
+
+
+# A few sanity checks.
+MODE=$(($APPEND_MODE + $CHECK_MODE + $DELETE_MODE + $UPDATE_MODE))
+if [ $MODE -eq 0 ]; then
+ fail "Please specify one of -a, -c, -d, or -u." \
+ "See \`rotcheck -h\` for help with usage."
+elif [ $MODE -gt 1 ]; then
+ fail "You can only use one of -a, -c, -d, or -u options." \
+ "See \`rotcheck -h\` for help with usage."
+elif [ $CHECK_MODE -eq 1 ] || [ $DELETE_MODE -eq 1 ]; then
+ if [ ! -f "$CHECKFILE" ]; then
+ fail "$CHECKFILE: No such file." \
+ "Try running \`rotcheck -a\` first, or see \`rotcheck -h\`."
+ fi
+elif ! command -v "$COMMAND" >/dev/null 2>/dev/null; then
+ fail "$COMMAND: command not found" \
+ "Try specifying a supported command using \`rotcheck -b COMMAND\`." \
+ "You may need to install GNU coreutils or BusyBox." \
+ "On *BSD, GNU coreutils commands begin with 'g', like 'gsha512sum'." \
+ "See \`rotcheck -h\` for help with usage."
+fi
+
+# When printing text to terminal, make sure it won't do anything unexpected.
+printf_sanitized() {
+ printf '%s' "$@" | tr -d '[:cntrl:]' | iconv -cs -f UTF-8 -t UTF-8
+ printf '\n'
+}
+
+verify_checksums() {
+ IGNORE="" ; [ $IGNORE_MISSING -eq 1 ] && IGNORE="--ignore-missing"
+ WARN="" ; [ $WARN_FORMATTING -eq 1 ] && WARN="-w"
+ $COMMAND -c $WARN $IGNORE -- "$CHECKFILE"
+}
+
+# Just verify checksums.
+if [ $CHECK_MODE -eq 1 ]; then
+ # Only GNU coreutils supports `--quiet`, so use `grep -v` instead.
+ # Unfortunately, pipefail isn't POSIX so to return the exit status from the
+ # checksum command, we have to be clever (aka crazy) with file descriptors
+ # and subshells instead.
+ if [ $VERBOSE -eq 1 ]; then
+ verify_checksums
+ exit $?
+ else
+ exec 4>&1
+ (
+ exec 3>&1
+ (
+ # 2>&1 preserves order of stdout/stderr.
+ verify_checksums 2>&1; printf '%d' $? 1>&3
+ ) | grep -Ev ': OK$' 1>&4
+ exec 3>&-
+ ) | ( read -r retval; exit $retval ); retval=$?
+ exec 4>&-
+ exit $retval
+ fi
+fi
+
+# Delete checksums for files that no longer exist.
+if [ $DELETE_MODE -eq 1 ]; then
+ i=1
+ for file in $(cut -d ' ' -f 3- -- "$CHECKFILE"); do
+ # `sed -i` isn't POSIX (nor is `mktemp`), so use `ex` instead.
+ if [ ! -f "$file" ]; then
+ cat << EOF | ex -s -- "$CHECKFILE"
+${i}d
+x
+EOF
+ # Print what checksums were deleted.
+ if [ $VERBOSE -eq 1 ]; then
+ printf '%s' "DELETED: "
+ printf_sanitized "$file"
+ fi
+ else
+ # Only increment the line number if we didn't delete a line.
+ i=$(($i + 1))
+ fi
+ done
+ exit $?
+fi
+
+# For safety and sanity, ignore all filenames that have control characters
+# like newline, tab, delete etc.
+find_safe() {
+ FIND_L=""
+ FIND_FOLLOW=""
+ if [ $FOLLOW_SYMLINKS -eq 1 ]; then
+ # Old versions of findutils don't have -L. Use it if available.
+ if find -L / -maxdepth 0 -type d >/dev/null 2>/dev/null; then
+ FIND_L="-L"
+ else
+ FIND_FOLLOW="-follow"
+ fi
+ fi
+
+ # POSIX find requires that you specify the search path either first
+ # or immediately after -H/-L. Use current directory by default unless
+ # user has specified a path.
+ FIND_DOT="./"
+ if [ $# -gt 0 ]; then
+ first_char="$(printf '%s' "$1" | cut -c 1)"
+ # Replace search path unless first arg is a non-path `find` option.
+ if [ "$first_char" != "-" ] \
+ && [ "$first_char" != "!" ] && [ "$first_char" != "(" ]; then
+ FIND_DOT=""
+ fi
+ fi
+
+ HIDDEN=""
+ [ $EXCLUDE_HIDDEN -eq 1 ] && HIDDEN='*/\.*'
+
+ find $FIND_L $FIND_DOT "$@" $FIND_FOLLOW \
+ -type f ! -path "$CHECKFILE" ! -path "$HIDDEN" \
+ ! -name "$(printf '*%b*' '\0001')" ! -name "$(printf '*%b*' '\0002')" \
+ ! -name "$(printf '*%b*' '\0003')" ! -name "$(printf '*%b*' '\0004')" \
+ ! -name "$(printf '*%b*' '\0005')" ! -name "$(printf '*%b*' '\0006')" \
+ ! -name "$(printf '*%b*' '\0007')" ! -name "$(printf '*%b*' '\0010')" \
+ ! -name "$(printf '*%b*' '\0011')" ! -name "$(printf '*%b*' '\0012')" \
+ ! -name "$(printf '*%b*' '\0013')" ! -name "$(printf '*%b*' '\0014')" \
+ ! -name "$(printf '*%b*' '\0015')" ! -name "$(printf '*%b*' '\0016')" \
+ ! -name "$(printf '*%b*' '\0017')" ! -name "$(printf '*%b*' '\0020')" \
+ ! -name "$(printf '*%b*' '\0021')" ! -name "$(printf '*%b*' '\0022')" \
+ ! -name "$(printf '*%b*' '\0023')" ! -name "$(printf '*%b*' '\0024')" \
+ ! -name "$(printf '*%b*' '\0025')" ! -name "$(printf '*%b*' '\0026')" \
+ ! -name "$(printf '*%b*' '\0027')" ! -name "$(printf '*%b*' '\0030')" \
+ ! -name "$(printf '*%b*' '\0031')" ! -name "$(printf '*%b*' '\0032')" \
+ ! -name "$(printf '*%b*' '\0033')" ! -name "$(printf '*%b*' '\0034')" \
+ ! -name "$(printf '*%b*' '\0035')" ! -name "$(printf '*%b*' '\0036')" \
+ ! -name "$(printf '*%b*' '\0037')" ! -name "$(printf '*%b*' '\0177')"
+}
+
+find_updated_files() {
+ if [ $FORCE_UPDATE -eq 1 ]; then
+ find_safe "$@"
+ else
+ find_safe "$@" -newer "$CHECKFILE"
+ fi
+}
+
+# This function could be replaced entirely with the much simpler:
+# cut -d ' ' -f 3- "$CHECKFILE" | grep -Fxn -- "$file" | cut -d ':' -f 1
+# But this function is slightly faster as it avoids passing huge chunks of text
+# (ie, the whole checksum file minus the first column) through a pipe.
+get_line_number() {
+ # Avoid `grep -E` as filename characters might get interpreted (eg, $).
+ for l in $(grep -Fn -- "$file" "$CHECKFILE" | cut -d ':' -f 1); do
+ if sed -n -e "${l}p" -- "$CHECKFILE" \
+ | cut -d ' ' -f 3- | grep -Fxq -- "$file" >/dev/null; then
+ printf '%d' "$l"
+ return 0
+ fi
+ done
+ printf '%d' "0"
+}
+
+umask 077
+# For files with a modification date newer than the checksum file, if there's
+# an existing checksum then update it. Otherwise append a new checksum.
+if [ $UPDATE_MODE -eq 1 ] && [ -f "$CHECKFILE" ]; then
+ for file in $(find_updated_files "$@"); do
+ line_num="$(get_line_number)"
+ if [ ${line_num:-0} -eq 0 ]; then
+ # No checksum yet, so append one.
+ $COMMAND -- "$file" >> "$CHECKFILE"
+ else
+ old="$(sed -n -e "${line_num}p" -- "$CHECKFILE" | cut -d ' ' -f 1)"
+ new="$($COMMAND -- "$file")"
+ # Should never happen, but double check these aren't empty:
+ if [ -z ${old:+x} ] || [ -z ${new:+x} ]; then
+ continue
+ fi
+ # `sed -i` isn't POSIX (nor is `mktemp`), so use `ex` instead.
+ if [ "$old" != "${new%% *}" ]; then
+ cat << EOF | ex -s -- "$CHECKFILE"
+${line_num}c
+$new
+.
+x
+EOF
+ # Bail immediately if something went wrong.
+ [ $? -ne 0 ] && fail "Failed to update checksum file."
+
+ # Print what checksums were changed.
+ if [ $VERBOSE -eq 1 ]; then
+ printf '%s' "CHANGED: "
+ printf_sanitized "$file"
+ fi
+ fi
+ fi
+ done
+fi
+
+# Append checksums for files that have no checksum yet.
+if [ $APPEND_MODE -eq 1 ] || [ $UPDATE_MODE -eq 1 ]; then
+ for file in $(find_safe "$@"); do
+ # Avoid `grep -E` as filename characters might get interpreted (eg, $).
+ # The first grep isn't strictly needed, but grep+cut+grep is faster
+ # than just cut+grep here.
+ if [ ! -f "$CHECKFILE" ] || ! grep -- "$file" "$CHECKFILE" \
+ | cut -d ' ' -f 3- | grep -Fxq -- "$file"; then
+ if ! $COMMAND -- "$file" >> "$CHECKFILE"; then
+ fail "Failed to write to checksum file."
+ fi
+
+ # Print what checksums were appended.
+ if [ $VERBOSE -eq 1 ]; then
+ printf '%s' "ADDED: "
+ printf_sanitized "$file"
+ fi
+ fi
+ done
+fi