summaryrefslogtreecommitdiff
path: root/bin
diff options
context:
space:
mode:
Diffstat (limited to 'bin')
-rwxr-xr-xbin/backup64
-rwxr-xr-xbin/backup-clean132
-rwxr-xr-xbin/byo13
-rwxr-xr-xbin/dv653
-rwxr-xr-xbin/fv6
-rwxr-xr-xbin/gauth5
-rwxr-xr-xbin/gemini132
-rwxr-xr-xbin/hdmi6
-rwxr-xr-xbin/icat2
-rwxr-xr-xbin/idot4
-rwxr-xr-xbin/lt1
-rwxr-xr-xbin/md136
-rwxr-xr-xbin/md2html193
-rwxr-xr-xbin/migrate_vimki58
-rwxr-xr-xbin/open11
-rwxr-xr-xbin/p4a3
-rwxr-xr-xbin/rdate11
-rwxr-xr-xbin/start_godoc5
-rwxr-xr-xbin/status227
-rwxr-xr-xbin/update_hosts28
-rwxr-xr-xbin/update_kernel_host13
-rwxr-xr-xbin/vcat8
-rwxr-xr-xbin/vimki56
-rwxr-xr-xbin/vm286
-rwxr-xr-xbin/w36
-rwxr-xr-xbin/wag335
-rwxr-xr-xbin/wol6
-rwxr-xr-xbin/xt4
-rwxr-xr-xbin/yoda4
29 files changed, 1483 insertions, 725 deletions
diff --git a/bin/backup b/bin/backup
index 72c69c3..9e73703 100755
--- a/bin/backup
+++ b/bin/backup
@@ -1,36 +1,54 @@
#!/bin/sh
-# Incremental backup using rsync(1).
-[ "$USER" = root ] || exec sudo "$0" "$@"
+# A backup a day keeps the doctor away.
-backup() {
- date=$(date +%Y%m%d_%H%M%S)
- last=$(rsync --list-only "$dest/" 2>/dev/null | cut -b 47- | tail -1)
+usage() {
+ echo "Usage: $0 [-nv] [[[user@]host]:dir]
- case $last in
- ([12]*) opt_link=--link-dest=../$last;;
- (*) opt_link=;;
- esac
+Incremental backup using rsync(1). If run as root, a full system
+backup is performed. Otherwise, the user's home directory is backed.
- rsync -HSxa$optv --exclude-from=$ignore $opt_link / /boot "$dest/$date"
-}
+Options:
+-n dry-run
+-v verbose
-dest=/.history
-ignore=/etc/backup/ignore
+Files:
+- $HOME/.backupignore exclude files from user backup (non root)
+- /etc/backupignore exclude files from system backup (root)
-while getopts :d:i:nv opt; do
+Environment:
+- BACKUP backup directory"
+}
+
+while getopts :nv opt; do
case $opt in
- (d) dest="$OPTARG" ;;
- (i) ignore="$OPTARG" ;;
(n|v) optv="$opt$optv" ;;
- (*) echo "Usage: $0 [-nv] [-d [host:]dir] [clean|diff]"; exit 1 ;;
+ (*) usage; exit 1 ;;
esac
done
shift $((OPTIND - 1))
-[ "$1" ] && cmd=$1 && shift || cmd=""
-case $cmd in
-(""|save) backup ;;
-(clean) exec backup-clean ${optv+-$optv} "$dest";;
-(diff) exec diffdir "$@";;
-esac
+BACKUP=${1:-$BACKUP}
+[ "$BACKUP" ] || { usage; exit 1; }
+
+[ "$USER" = root ] &&
+ ignore=/etc/backupignore volumes='/ /boot' ||
+ ignore="$HOME/.backupignore" volumes="$HOME"
+[ -f "$ignore" ] && ignore="--exclude-from=$ignore" || ignore=
+
+now=$(date +%F)
+opt_link=''
+past=$(rsync --list-only "$BACKUP/" 2>/dev/null | awk '{print $NF}' | sort -r)
+for f in $past
+do
+ case $f in
+ ([0-9][0-9][0-9][0-9]-*) ;;
+ (*) continue ;;
+ esac
+ [ "$f" = "$now" ] && continue
+ opt_link="--link-dest=../$f"
+ break
+done
+
+echo "# Backup $volumes to $BACKUP/$now"
+exec rsync -HSXxa$optv $ignore $opt_link $volumes "$BACKUP/$now"
diff --git a/bin/backup-clean b/bin/backup-clean
index 30d25db..34e6d97 100755
--- a/bin/backup-clean
+++ b/bin/backup-clean
@@ -1,89 +1,67 @@
#!/bin/sh
-# backup garbage collector
-[ "$USER" = root ] || exec sudo "$0" "$@"
-# Durations, in number of seconds.
-hd=3600 # hour duration: 60s * 60
-h12=43200 # 12h: hd * 12
-dd=86400 # day duration: hd * 24
-wd=604800 # week duration: dd * 7
-md=2592000 # month duration: dd * 30
-yd=31557600 # year duration: dd * 365.25
+usage() {
+ echo "Usage: $0 [-nv] [[[user@]host:]dir]
-now=$(date +'%Y%m%d_%H%M%S')
+$0 removes old backups and keeps:
+- backups for the current day
+- 1 backup per past day for the current month
+- 1 backup per past month for the current year
+- 1 backup per past year
-# The following works only for busybox date, not coreutils
-# Kept for reference only.
-#date2ts() { d=${1%_*} t=${1#*_}; date -d "$d${t%??}" +%s; }
+Options:
+-n dry run
+-v verbose
-# Convert date to timestamp, using date from GNU coreutils,
-# works also with busybox date.
-date2ts() {
- t=$1; r=${t#????}; Y=${t%$r} # Year (with century)
- t=$r; r=${t#??}; m=${t%$r} # Month
- t=$r; r=${t#??}; d=${t%$r} # Day
- t=${r#_}; r=${t#??}; H=${t%$r} # Hour
- t=$r; r=${t#??}; M=${t%$r} # Minute
- S=${t#??} # Second
- date -d "$Y-$m-$d $H:$M:$S" +%s
- #date -jf "%Y-%m-%d %H:%M:%S" "$Y-$m-$d $H:$M:$S" +%s # BSD, MacOS (not tested)
+Environment variables:
+- BACKUP - backup directory"
}
-ts2date() { date -d "@$1" +'%Y%m%d_%H%M%S'; }
-
-tsn=$(date2ts "$now")
-
-# Minimal retention delay in seconds, according to backup age,
-# implemented using POSIX shell arithmetic.
-retention_delay() {
- d=$((tsn - $1))
- if [ $((d < h12)) = 1 ]; then
- r=0 # keep all backups in the last 12 hours
- elif [ $((d < dd)) = 1 ]; then
- r=$hd # keep 1 backup per hour in the last day
- elif [ $((d < wd)) = 1 ]; then
- r=$dd # keep 1 backup per day in the last week
- elif [ $((d < md)) = 1 ]; then
- r=$wd # keep 1 backup per week in the last month
- elif [ $((d < yd)) = 1 ]; then
- r=$md # keep 1 backup per month in the last year
- else
- r=$yd # keep 1 backup per year in the previous years
- fi
- echo $r
-}
-
-dest=/.history
-while getopts :d:nv opt; do
+while getopts :nv opt; do
case $opt in
- (d) tsn=$(date2ts "$OPTARG") ;;
- (n) optn=1 ;;
- (v) optv=1 ;;
- (*) echo "Usage: $0 [-nv] [dir]"; exit 1 ;;
+ (n) optn=echo ;;
+ (v) optv=-t ;;
+ (*) usage; exit 1 ;;
esac
done
-shift $((OPTIND - 1))
-[ "$1" ] && dest=$1
+shift $((OPTIND -1))
+
+BACKUP=${1:-$BACKUP}
+[ "$BACKUP" ] || { usage; exit 1; }
-# Sorted list of backups, most recent first.
-lbu=$(ls -rv "$dest")
-lasy=${lbu##*
+host=${BACKUP%:*} dir=${BACKUP#*:}
+ls='ls -r' rm="xargs -r $optv $optn rm -rf"
+[ "$host" = "$dir" ] || ls="ssh $host $ls" rm="ssh $host $rm"
+
+echo "# Cleaning backups on $BACKUP"
+
+$ls "$dir" |
+awk -v now=$(date +%Y-%m-%d) -v dir="$dir" '
+BEGIN {
+ yn = substr(now, 1, 4) # Year now
+ mn = substr(now, 6, 2) # Month now
+ dn = substr(now, 9, 2) # Day now
}
-for d in $lbu; do
- tsc=$(date2ts "$d")
- if ! [ "$tsp" ]; then
- [ "$optv" ] && echo "keep $dest/$d"
- tsp=$tsc
- continue
- fi
- mrd=$(retention_delay "$tsp")
- dp=$((tsp - tsc))
- #if [ $((dp < mrd)) = 1 ]; then
- if [ "$d" != "last" -a $((dp < mrd)) = 1 ]; then
- [ "$optv" ] && echo "delete $dest/$d"
- [ "$optn" ] || rm -rf "${dest:?}/$d"
- else
- [ "$optv" ] && echo "keep $dest/$d"
- tsp=$tsc
- fi
-done
+{
+ yb = substr($0, 1, 4) # Year backup
+ mb = substr($0, 6, 2) # Month backup
+ db = substr($0, 9, 2) # Day backup
+ $0 = dir "/" $0
+
+ dy = yn - yb
+ dm = dy * 12 + mn - mb
+ # if (yb != yn) {
+ if (dm > 12) {
+ if (yb in yearly) print; else yearly[yb] = 1
+ next
+ }
+ # if (mb != mn) {
+ dd = dm * 30 + dn - db
+ if (dd > 30) {
+ if (mb in monthly) print; else monthly[mb] = 1
+ next
+ }
+ if (db != dn) {
+ if (db in dayly) print; else dayly[db] = 1
+ }
+}' | $rm
diff --git a/bin/byo b/bin/byo
index bc09fb8..33ba3b7 100755
--- a/bin/byo
+++ b/bin/byo
@@ -2,23 +2,26 @@
# Backup to yoda
[ "$USER" = root ] || exec sudo "$0" "$@"
-usage='Usage: byo [-cdk] [/dev/sda2]
+usage='Usage: byo [-cdkx] [/dev/sda2]
Backup local disk to yoda external disk.
Options:
-c close and umount yoda
-d dedupe yoda
-k do not umount and close yoda
+ -x enable debug traces
'
-while getopts :Ccdk opt; do
+while getopts :Ccdkx opt; do
case $opt in
- (C|c|d|k) eval "opt$opt=$opt" ;;
+ (C|c|d|k|x) eval "opt$opt=$opt" ;;
(*) printf %s "$usage"; exit 1 ;;
esac
done
shift $((OPTIND - 1))
+[ "$optx" ] && set -x
+
dev=$1
yoda_uuid='8c463221-6bb7-414e-9060-c9570bb3a6bb'
[ "$dev" ] || dev=$(blkid --uuid "$yoda_uuid")
@@ -26,8 +29,8 @@ yoda_uuid='8c463221-6bb7-414e-9060-c9570bb3a6bb'
dest=/mnt/backup/$(hostname)
[ -b /dev/mapper/yoda ] && noclose=1 || cryptsetup open "$dev" yoda
findmnt /dev/mapper/yoda /mnt >/dev/null && noumount=1 || mount /dev/mapper/yoda /mnt
-time backup -v -d "$dest"
-[ ! "$optC" ] || backup -v -d "$dest" clean
+time backup -v "$dest"
+# [ ! "$optC" ] || backup -v -d "$dest" clean
[ ! "$optd" ] || time duperemove -drh --hashfile="$dest/.hashfile" "$dest"
df -h / "$dest"
ls -v "$dest"
diff --git a/bin/dv b/bin/dv
index 0737295..39164db 100755
--- a/bin/dv
+++ b/bin/dv
@@ -1,556 +1,213 @@
#!/bin/sh
-# Local dv (aka sim)
+# - Usage: dv command [options] [args]
+# - dv is a dumb version management tool.
+# - Options:
+# - -a num set ancestor level (default 1)
+# - -n dry-run mode, no changes are made
+# - -v verbose mode
+# - Commands:
-dv_version='dv-0.23 Copyright 2013-2014 Marc Vertes, Philippe Bergheaud'
unset CDPATH
export LC_ALL=C IFS='
'
-
-anc() {
- usage 'anc [Version]' 'print ancestor' && return
- getdv .dv || die no item
- case $1 in
- (""|.|$PWD)
- [ "$Opt_N" ] && Opt_N=$(($Opt_N - 1))
- [ "$Opt_N" ] && info anc $R2 $Opt_N || echo $R2
+version=dv-0.2
+dvlib=${DVLIB:-$HOME/dvlib}
+script="$(cd "$(dirname "$0")" && pwd)/${0##*/}"
+aopt=1
+
+case $(uname -o) in
+ (Android|Linux)
+ diffopt='--color=always'
+ lsopt='-v --color'
;;
- (*) info anc $1 ${Opt_N:-1};;
- esac
-}
+ (Darwin)
+ diffopt='--color=always'
+ lsopt='-G'
+ ;;
+ (*BSD)
+ ;;
+esac
-cache() {
- isremote "$1" || return
- o=$1 _d=$cacheprefix/$1; pd=${_d%/*}; ppd=${pd%/*} t=$pd/.${_d##*/}
- [ -d "$ppd" ] || mkdir -p "$ppd"
- [ -L "$ppd/.dv." ] || ln -s . "$ppd/.dv."
- [ -f "$_d/.dv" ] && return
- [ -d "$pd" ] || mkdir -p "$pd"
- set -- $pd/*; shift $(($# - 1))
- [ -d "$1" ] && ropt="--link-dest=../${1##*/}" || ropt=
- rsync -aDS $ropt "$o/" "$t" && mv "$t" "$_d"
+# anc [-a num] [dir] - prints the current ancestor of dir or current.
+anc() {
+ set -- "$(dvdir "$1")"
+ for _ in $(seq "$aopt"); do
+ set -- "$1/.dv/anc"
+ done
+ [ -L "$1" ] && readlink -f "$1"
}
-client() {
- usage 'client [Version]' 'print clients' && return
- getdv .dv || die no item
+# clone [-nv] v1 [dir] - clones an iterm version into dir (default: item).
+clone() {
case $1 in
- (""|.|$PWD)
- while getpdir .dv ..
- do
- cd "$R1"
- getdv ".dv"
- [ "$OptM" ] && echo "$R2 -> $PWD" || echo "$R2"
- ((Opt_N = Opt_N-1)) || break
- done ;;
- (*) info cli $1 ${Opt_N:-9999} | sort -u ;;
+ ('') die 'missing argument' ;;
+ (*[/.-]*) set -- "$(path "$1")" "$2" ;;
+ (*) set -- "$(head "$1")" "$2" ;;
esac
+ [ "$2" ] || { set -- "$1" "${1##*/}"; set -- "$1" "${2%%-*}"; }
+ [ -d "$1" ] || die "not found: $1"
+ rsync ${vopt:+"$vopt"} ${nopt:+"$nopt"} -a "$1/" "$2/"
+ rm -f "$2/.dv/anc" && ln -s "$1" "$2/.dv/anc"
}
-clone() {
- usage 'clone [-A Author] [-m Msg] Version [Dir]' \
- 'Copy version to dir' && return
- [ "$1" ] || die missing argument
- [ "$3" ] && die too many arguments
- getpdir .dv . && root=$R1 && getdv $root/.dv && lib=$R1 item=${R2%/*}
- new_version=${1##*/}
- if [ "$new_version" = "$1" ]
- then
- getdv .dv && lib=$R1 new_item=${R2%/*} || die no item
- else
- v=${1%/$new_version}
- new_item=${v##*/}
- [ "$new_item" = "$v" ] || new_lib=${v%/$new_item}
- fi
- [ "$PWD" = "$root" ] && [ "$item" != "$new_item" ] &&
- die "already a clone of $item"
- lib=${lib:-$new_lib}
- [ "$lib" ] || die no lib
- [ "$new_lib" -a "$new_lib" != "$lib" ] && die "already in lib: $lib"
- if [ "$new_version" = 0 ] # Init item in lib
- then
- initlib "$lib"
- getdvinfo "$lib" || die "could not lock $lib"
- mkdir -p /tmp/dv.$$/$new_item/0
- dv_write $new_item/0 $new_item/0 >/tmp/dv.$$/$new_item/0/.dv
- { cat /tmp/dv.$$/$new_item/0/.dv; echo; } >>/tmp/dv.$$/.dvinfo.0
- ln -f /tmp/dv.$$/.dvinfo.0 /tmp/dv.$$/.dvinfo.1
- rsync -a /tmp/dv.$$/ "$lib"
- fi
- if [ ! "$2" ] # Use the item name as default directory (a la git)
- then
- set -- "$1" "${1%/*}"
- set -- "$1" "${2##*/}"
- echo cloning into "$2"
- fi
- [ ! -d "$2" ] && mkdir "$2"
- cd "$2" || die cannot chdir to "$2"
- # Rename existing supplier directories to allow supplier symlinks
- find . -name .dv | while read l
- do
- l=${l%/.dv} && [ "$l" = "." ] && continue
- mv "$l" "$l.dvold"
- done
- # Sync from lib, starting from most client item, up to last supplier
- d=. s=$new_item/$new_version
- while [ "$d" ]
- do
- [ -L "$d" ] && {
- rm -f "$d"
- [ -d "$d.dvold" ] && mv "$d.dvold" "$d"
- }
- cache "$lib/$s" && pref=$cacheprefix/ || pref=
- rsync -aDS "$pref$lib/$s/" "$d"
- dv_header "$d" Anc "$s"
- dv_header "$d" Lib "$lib"
- # Find next symlink pointing to a supplier
- d= l=
- eval "$(find . -type l | while read -r l
- do
- f=$(readlink "$l")
- case $f in
- (*/.dv./*) echo "d=\"$l\" s=\"${f#*/.dv./}\""
- break;;
- esac
- done)"
+# desc - prints the descendants of a version
+desc() {
+ set -- "$(path "$1")"
+ set -- "$1" "${1%/*}"
+ set -- "$1" "${2##*/}"
+ for a in "$dvlib/$2"/*/.dv/anc*; do
+ [ "$a" -ef "$1" ] && echo "${a%/*/*}"
done
}
-desc() {
- usage 'desc [Version]' 'print descendants' && return
- getdv .dv || die no item
- case $1 in
- (""|.|$PWD) ;;
- (*) info desc $1 ;;
- esac
+die() {
+ echo "$@" >&2
+ exit 1
}
-die() { echo "$0: fatal: $@" >&2; exit 1; }
-
+# diff [-a num] [[v1] v2] - prints differences (default: current and ancestor).
diff() {
- usage 'diff [-x pat] [-F File] [V1 [V2]]' 'Print differences' && return
- getpdir .dv && getdv "$R1/.dv" || die not in a clone
- lib=$R1 item=${R2%/*} old=${R2#*/}
- [ "$lib" = "" ] && [ -L "../../.dv." ] && lib=${PWD%/*/*}
- if [ "$2" ]
- then
- case $2 in
- (.) new=. new_is_clone=1 ;;
- (*) new=$lib/$item/$2 ;;
- esac
- cache "$new" && np=$cacheprefix/ || np=
- old=$lib/$item/$1
- elif [ "$1" ]
- then
- case $1 in
- (.) new=. new_is_clone=1 ;;
- (*) new=$lib/$item/$1 ;;
- esac
- cache "$new" && np=$cacheprefix/ || np=
- getdv "$np$new/.dv" && old=${R1:-$lib}/$R2
+ if [ "$2" ]; then
+ set -- "$(path "$2")" "$(path "$1")"
+ elif [ "$1" ]; then
+ set -- "$(path "$1")" "$(anc "$(path "$1")")"
else
- new=$PWD new_is_clone=1
- cache "$new" && np=$cacheprefix/ || np=
- getdv "$np$new/.dv" && old=${R1:-$lib}/$R2
+ set -- "$(dvdir)" "$(anc)"
fi
- cache "$old" && op=$cacheprefix/ || op=
- [ "$Opts" ] || printf "old %s\nnew %s\n" "$old" "$new"
- # Itemized diff for all suppliers, deepest first
- cd "$np$new" && for f in $(find . -type f -name .dv | sort -r)
- do
- d=${f%/.dv}
- [ "$d" = . ] && prefix= || prefix=${d#./}/
- getdv "$f" && cache "$lib/$R2"
- [ "$new_is_clone" ] && dest=$np$d || dest=$prefix$d
- diffdir "$op$lib/$R2" "$dest"
- Optx="$Optx --exclude=${d##*/}"
- done
-}
-
-# Usage: diffdir oldpath newpath
-# Print differences between 2 directories
-diffdir() {
- [ -f "$2/.dvignore" ] && xf=--exclude-from=$2/.dvignore || xf=
- rsync -aDSniv $xf --delete --exclude=".dv.*/" $Optx "$2/" "$1" |
- awk -v OptF="${OptF#./}" -v prefix=$prefix '
- NF == 0 {exit}
- NR < 2 || /\/*\.dv$/ || /\/$/ {next}
- # Match an itemized status for all versions of rsync -i
- $1 !~ /^[<>ch.*][fdLDS+][.+?cstpoguaxz]+$/ {next}
- {key = $1; file = substr($0, length(key) + 2)}
- OptF && OptF != file {next}
- key == "*deleting" {print "deleted " prefix file; next}
- substr(key, 3, 7) == "+++++++" {print "created " prefix file; next}
- { # Avoid false positive if only mtime is changed.
- of = "'$1'/" file; gsub("'\''", "'\'\\\\\'\''", of)
- nf = "'$2'/" file; gsub("'\''", "'\'\\\\\'\''", nf)
- if (substr(key, 2, 1) == "L") { # Symlink
- src = target = file
- sub(/.* -> /, "", target);
- sub(/ -> .*/, "", src);
- "readlink '$2'/" src | getline otarget
- if (target != otarget)
- print "changed " src
- } else if (system("cmp -s '\''" of "'\'\ \''" nf "'\''"))
- print "changed " file
- }'
- # Or: scan key for file/link size, checksum or permission change
- # rsync -c is required
- #key ~ /[cps]/ { print "changed " prefix file }'
+ [ "$2" ] && command diff $diffopt -U 3 -x .dv "$2" "$1" | less -F
}
-dv_write() {
- cat <<- EOT
- From $(username)
- Date: $(date +"%F %T %z")
- Version: $1
- Anc: $2
- EOT
-
- [ "$OptA" ] && echo "Author: $OptA"
- printf "\n%s\n" "$Optm"
+# dvdir prints the dir containing .dv.
+dvdir() {
+ set -- "$(realpath "${1:-.}")"
+ while [ "$1" ]; do
+ [ -d "$1/.dv" ] && echo "$1" && return
+ set -- "${1%/*}"
+ done
}
-dv_header() {
- awk -v val="$3" '/^'$2':/ {print "'$2': " val; done = 1; next}
- NF == 0 && done == 0 {print "'$2': " val; done = 1}
- {print}' $1/.dv >$1/.dv.$$ && mv $1/.dv.$$ $1/.dv
+# head - prints the branch head versions.
+head() {
+ for v in "$dvlib/${1:-$(name)}/"*; do
+ [ "$(desc "$v")" ] || echo "$v"
+ done
}
-# Usage: getdv path
-# Return lib in R1, anc in R2, version in R3
-getdv() {
- R1= R2=
- while read -r line
- do
+# help - prints this help text.
+help() {
+ while read -r line; do
case $line in
- ("Lib: "*) R1=${line#Lib: };;
- ("Anc: "*) R2=${line#Anc: };;
- ("Version: "*) R3=${line#Version: };;
+ (\#*\ -\ *) ;;
+ (*) continue ;;
esac
- done <$1
- [ "$R1" -o "$R2" ]
+ set -- "${line% - *}" "${line#* - }"
+ [ "$1" = '#' ] || printf " %-26s" "${1#\# }"
+ echo "$2"
+ done < "$script"
}
-# Usage: getdvinfo lib
-# Get exclusive write access to a dvlib, for new version commit
-getdvinfo() {
- [ -d /tmp/dv.$$ ] || mkdir /tmp/dv.$$
- [ "$Optn" ] && { rsync "$1/.dvinfo.1" /tmp/dv.$$/.dvinfo.0; return; }
- t=0
- for i in 1 2 3 4 5
- do
- rsync --remove-source-files "$1/.dvinfo.0" /tmp/dv.$$/ && break
- t=$(($t + $i)) && sleep $t
- done
- test -f /tmp/dv.$$/.dvinfo.0
+# incv increments version.
+incv() {
+ set -- "$1" "${1##*[-.a-z_A-Z]}"
+ set -- "${1%"$2"}" "$2"
+ echo "$1$(($2 + 1))"
}
-putdvinfo() {
- [ "$Optn" ] && return
- ln -f /tmp/dv.$$/.dvinfo.0 /tmp/dv.$$/.dvinfo.1
- rsync -a /tmp/dv.$$/.dvinfo.[01] "$1/"
- isremote "$1" && cp /tmp/dv.$$/.dvinfo.0 "$cacheprefix/$1/"
+# init - creates an initial clone in the current directory.
+init() {
+ [ "$(dvdir)" ] && die "already initialized: $(dvdir)"
+ mkdir .dv
}
-# Usage: getpdir file [path]
-# Return absolute parent dir of path or PWD containing file
-getpdir() {
- R1="$([ -d "${2:-.}" ] && cd "${2:-.}" && pwd)"
- while [ "$R1" ]
- do
- [ -f "$R1/$1" ] && return || R1=${R1%/*}
- done
- return 1
-}
+# ls [item] - lists dvlib.
+ls() (
+ cd "$dvlib" && command ls $lsopt "$@"
+)
-# Usage: getprd file [path]
-# Return relative path to parent directory containing file
-getprd() {
- R2="$(cd "${2:-.}" && pwd)"; R2=${R2%/*} R1=..
- while [ "$R2" ]
- do
- [ -f "$R2/$1" ] && return || R2=${R2%/*} R1=$R1/..
- done
- return 1
+# name prints the item name.
+name() {
+ set -- "$(dvdir)"
+ [ "$1" ] && echo "${1##*/}" || die "not a dv item, no .dv found"
}
-info() {
- usage info && return
- case $2 in
- (*:*) lib=${2%/*/*} ;;
- (*) getpdir .dv && getdv $R1/.dv && lib=$R1 || die no dvlib found
- esac
- case $2 in # $2 specifies:
- (*/*/*) v=${2%/*/*}; v=${2#$v/} ;; # lib/item/version
- (*/*) v=$2 ;; # item/version
- (*) v=${R2%/*}/$2 ;; # version
+# path prints the version path, if exist.
+path() {
+ case $1 in
+ (*/*|.) dvdir "$1"; return ;;
+ ([0-9]*) set -- "$(name)-$1" ;;
esac
- isremote "$lib" && {
- p=$cacheprefix
- [ -d "$p/$lib" ] || mkdir -p "$p/$lib"
- [ "$Optl" ] || rsync -a "$lib/.dvinfo.1" "$p/$lib/"
- } || p=
- info_query $1 $v $3 <$p/$lib/.dvinfo.1
-}
-
-info_query()
-{
- awk -v arg0=$1 -v arg1=$2 -v arg2=$3 '
- /^From / {
- if (v) msg[v] = var["Body"]
- delete var
- var["From"] = substr($0, 6)
- header = 1
- next
- }
- header == 1 && NF == 0 {
- v = var["Version"]; a = var["Anc"]; s = var["Sup"]
- if (a != v) {
- anc[v] = a; desc[a] = desc[a] ? desc[a] " " v : v
- }
- n = split(s, as)
- for (i = 1; i <= n; i++) {
- cli[as[i]] = cli[as[i]] ? cli[as[i]] " " v : v
- }
- sup[v] = s; msg[v] = var["Msg"]; date[v] = var["Date"]
- if (var["Root"]) root[v] = var["Root"]
- header = 0
- next
- }
- header == 1 {
- i = index($0, ":")
- var[substr($0, 1, i-1)] = substr($0, i+2)
- next
- }
- header == 0 {
- if ($0 ~ />+From /) sub(/>/, "")
- var["Body"] = var["Body"] ? var["Body"] "\n" $0 : $0
- }
- END {
- if (v) msg[v] = var["Body"]
- if (arg0 == "graph") anc_graph()
- else if (arg0 == "anc") query_anc(arg1, arg2)
- else if (arg0 == "sup") query_sup(arg1, arg2)
- else if (arg0 == "cli") query_cli(arg1, arg2)
- else if (arg0 == "log") print msg[arg1]
- else if (arg0 == "from") print from[arg1]
- else if (arg0 == "date") print date[arg1]
- else if (arg0 == "desc") printl(desc[arg1])
- }
- function anc_graph() {
- print "digraph G {"
- for (v in anc) print "\"" anc[v] "\" -> \"" v "\""
- print "}"
- }
- function query_anc(v, n) { while (n-- > 0) v = anc[v]; print v }
- function query_cli(v, n) {
- while (n-- > 0 && v != "") {
- num = split(v, av); v = ""
- for (i = 1; i <= num; i++) {
- printl(cli[av[i]])
- v = v ? v " " cli[av[i]] : cli[av[i]]
- }
- }
- }
- function query_sup(v, n) {
- while (n-- > 0 && v != "") {
- num = split(v, av); v = ""
- for (i = 1; i <= num; i++) {
- printl(sup[av[i]])
- v = v ? v " " sup[av[i]] : sup[av[i]]
- }
- }
- }
- function printl(l, i, n) {
- n = split(l, al)
- for (i = 1; i <= n; i++)
- if (root[al[i]])
- print al[i] " -> '"$PWD/"'" root[al[i]]
- else
- print al[i]
- }'
+ set -- "${1%%-*}" "${1#*-}"
+ [ -d "$dvlib/$1/$1-$2" ] && echo "$dvlib/$1/$1-$2"
}
-initlib() {
- rsync --list-only "$1/.dv." >/dev/null 2>&1 && return
- mkdir -p /tmp/dv.$$
- ln -s . /tmp/dv.$$/.dv.
- >/tmp/dv.$$/.dvinfo.0
- >/tmp/dv.$$/.dvinfo.1
- chmod g+w /tmp/dv.$$/.dvinfo.[01]
- rsync -a /tmp/dv.$$/ "$1"
-}
+# save [-nv] [v1] - creates a new version from clone.
+save() {
+ [ "$1" ] && case $1 in (*[0-9]) ;; (*) die "invalid version: $1" ;; esac
+ set -- "$(dvdir)" "$(aopt=1 anc)" "$(name)" "${1#[a-zA-Z_]*-}"
+ set -- "$1" "$2" "$3" "${4:-$(incv "${2##*/[a-zA-Z_]*-}")}"
+ echo "$dvlib/$3/$3-$4"
+ command diff -q -x .dv "$2" "$1" >/dev/null 2>&1 && die 'no changes, abort'
+ [ "$nopt" ] && return
-isremote() { case $1 in (*:*) return;; esac; return 1; }
+ # Update ancestor in clone then copy in dvlib.
+ rm -f "$1/.dv/anc" && [ "$2" ] && ln -s "../../${2##*/}" "$1/.dv/anc"
+ rsync ${vopt:+"$vopt"} -a ${2:+--link-dest="$2"} --mkpath "$1/" "$dvlib/$3/$3-$4/"
-help_all() {
- printf "$dv_version\nUsage: dv command [options] [args]\n"
- Opth=1; for c in $Cmdlist; do $c; done
+ # After save, clone ancestor points to new saved version.
+ rm -f "$1/.dv/anc" && ln -s "$dvlib/$3/$3-$4" "$1/.dv/anc"
}
-newversion() {
- [ "$OptV" ] && R1=$OptV && return
- case $OptB in
- ('') R1=$1 ;;
- (*[-/]*) die 'illegal character [-/] in branch name' ;;
- (*[0-9.]) die 'illegal end character [0-9.] in branch name' ;;
- (*) case $1 in (*-${OptB}[1-9]*) R1=$1;; (*) R1=$1-${OptB}0;; esac ;;
- esac
- R2=${R1##*[!0-9]}
- optb=$Optb
- [ "$optb" ] && R3=.1 optb=${optb%b} || R3= R1=${R1%$R2}$(($R2 + 1))
- while [ "${optb}" ]; do optb=${optb%b} R3=.0$R3; done
- R1=$R1$R3
- while grep -q "^Version: $item/$R1\$" /tmp/dv.$$/.dvinfo.0
- do
- R2=${R1##*[!0-9]}
- [ "$R2" ] && R1=${R1%$R2}$(($R2 - 1)).1 || R1=${R1}1
- done
+# status - prints dv informations.
+status() {
+ echo "lib: $dvlib"
+ set -- "$(dvdir)" "$(anc)"
+ [ "$1" ] || die "not a dv item, no .dv found"
+ echo "root: $1"
+ [ "$2" ] || die "no ancestor found, please save it first"
+ echo "anc: $2"
+ command diff -q -x .dv "$2" "$1"
}
-patch() {
- usage 'patch [-x pat] [-F File] [Dir|Version]' \
- 'Print patch diff from ancestor' && return
- diff "$@" | awk -v wdflag="-v${OptD:+3}${OptN:+1}${OptO:+2}" '
- { key = $1; file = substr($0, length(key) + 2) }
- key == "old" { old = (file ~ /:/ ? "'$cacheprefix'/" : "") file; next }
- key == "new" { new = (file ~ /:/ ? "'$cacheprefix'/" : "") file; next }
- {
- of = old "/" file; gsub("'\''", "'\'\\\\\'\''", of)
- nf = new "/" file; gsub("'\''", "'\'\\\\\'\''", nf)
- system("diff -Naup '\''" of "'\'\ \''" nf "'\''; echo")
- }'
+# sync [url] - synchronize a remote dvlib.
+sync() {
+ set -- "${1:-$(cat "$dvlib/.url" 2>/dev/null)}"
+ [ "$1" ] || die "missing url"
+ [ "$(cat "$dvlib/.url" 2>/dev/null)" = "$1" ] || echo "$1" > "$dvlib/.url"
+ rsync ${vopt:+"$vopt"} ${nopt:+"$nopt"} -aH --mkpath "$1/" "$dvlib/"
+ rsync ${vopt:+"$vopt"} ${nopt:+"$nopt"} -aH --mkpath "$dvlib/" "$1/"
}
-save() {
- usage 'save [-bln] [-A Author] [-m msg] [-x pat] [-B Branch|-V Version] [Dir]'\
- 'Save dir into a new version' && return
- [ "$2" ] && die 'too many arguments'
- getpdir .dv "$1" || die "not in a clone: ${1:-$PWD}" && root=$R1
- cd "$root"
- getdv .dv && lib=$R1 && getdvinfo "$lib"
- # Process supplier subdirs from the deepest up to "." (most client)
- for d in $(find . -type f -name .dv | sort -r)
- do
- d=${d%/.dv}
- cd "$root/$d"
- OptF= save1 "$d" && nanc=$R1 && echo $nanc
- [ "$d" = . ] && continue # not a supplier, done
-
- # Replace supplier subdir by symlink in lib
- getprd .dv && s=$R1/../.dv./$nanc
- t=${d##*/}; t=.dv.$t
- mv "$root/$d" "$root/${d%/*}/$t"
- ln -s "$s" "$root/$d"
- done
- putdvinfo "$lib"
- # Restore supplier subdirs and remove links to lib, deepest first
- find . -type d -name ".dv.*" | sort -r | while read d
- do
- t=${d##*/.dv.}; t=${d%/*}/$t
- rm -f "$t" && mv "$d" "$t"
- done
+# version - prints the current version of dv.
+version() {
+ echo "$version"
}
-save1() {
- getdv .dv && lib=$R1 anc=$R2 && item=${anc%/*} old=${anc#*/}
- [ "$(diffdir "$lib/$anc" .)" ] || { R1=$anc; return; }
- newversion $old && new=$item/$R1
- [ "$Optn" ] || dv_write "$new" "$anc" >.dv
- # Compute list of direct suppliers, for caching in .dvinfo.0
- lsup=$(find . -type l | while read -r l
- do
- case $l in (*/.dv.*) continue ;; esac
- f=$(readlink "$l")
- case $f in (*/.dv./*) printf "%s " ${f#*/.dv./} ;; esac
- done)
- [ ! "$Optn" ] && [ "$lsup" ] && dv_header . Sup "$lsup"
- { cat .dv; echo; } >>/tmp/dv.$$/.dvinfo.0
- [ -f ".dvignore" ] && xf=--exclude-from=.dvignore || xf=
- rsync -aDS$Optn$Optv --link-dest=../$old $xf --exclude=".dv.*/" \
- ./ "$lib/$new"
- R1=$new
- [ "$Optn" ] && return
- isremote "$lib" && rsync -aDS --link-dest=../$old --exclude=".dv.*/" \
- ./ "$cacheprefix/$lib/$new"
- dv_header . Anc "$new"
- dv_header . Lib "$lib"
-}
-
-supplier() {
- usage 'supplier [Version]' 'print suppliers' && return
- getdv .dv || die no item
+# Main program starts here.
+[ "$DVDEBUG" ] && set -x ||
case $1 in
- (""|.|$PWD) # create a temporary dvinfo file, query it
- V=$R2; find . -name .dv |
- while read dv
- do
- [ "$dv" = "./.dv" ] && continue
- # get the supplier
- getdv "$dv"; v=$R2; dv=${dv%/.dv}
- # get the client
- getpdir .dv "${dv%/*}"; getdv "$R1/.dv"
- # declare the client as supplier
- printf "From \nVersion: %s\nSup: %s\n" "$v" "$R2"
- [ "$OptM" ] && echo "Root: ${dv#./}"; echo
- done | info_query cli $V ${Opt_N:-9999} ;;
- (*) info sup $1 ${Opt_N:-9999} ;;
- esac | sort -u
-}
-
-usage() { [ "$Opth" ] && printf " %-34s %s\n" "$1" "$2"; }
-
-username() {
- case $(uname -s) in
- (Darwin) id -P | awk -v FS=: '{print $8}' ;;
- (*) awk -v FS=[:,] '/^'$USER':/ {print $5; exit}' /etc/passwd ;;
+ (anc|clone|desc|diff|head|help|init|ls|path|save|status|sync|version) ;;
+ (*) help; exit 1;;
esac
-}
-
-wdiff() {
- usage 'wdiff [-DNO] [-x pat] [-F File] [Dir|Version]' \
- 'Print word diffs from ancestor' && return
- diff "$@" |
- awk -v wdflag="-v${OptD:+3}${OptN:+1}${OptO:+2}" '
- { key = $1; file = substr($0, length(key) + 2) }
- key == "old" { old = (file ~ /:/ ? "'$cacheprefix'/" : "") file; next }
- key == "new" { new = (file ~ /:/ ? "'$cacheprefix'/" : "") file; next }
- {
- of = old "/" file; gsub("'\''", "'\'\\\\\'\''", of)
- nf = new "/" file; gsub("'\''", "'\'\\\\\'\''", nf)
- system("wd " wdflag " '\''" of "'\'\ \''" nf "'\''; echo")
- }' | less -FCimnqGrX -j5 -h0 +/'\[0'
-}
-
-cacheprefix=$HOME/.cache/dv
-Cmdlist='anc client clone desc diff info patch save supplier wdiff'
-while getopts :nvV opt # Parse global options
-do
+cmd="$1"
+shift
+while getopts :a:nv opt; do
case $opt in
- (V) echo "$dv_version"; exit ;;
- (*) help_all; exit ;;
+ (a) aopt="$OPTARG" ;;
+ (n) nopt=-n ;;
+ (v) vopt=-v ;;
+ (*) help; exit ;;
esac
done
shift $((OPTIND - 1))
-[ $1 ] && C=$1 && shift 1 || { help_all; exit 1; }
-for c in $Cmdlist
-do
- case $c in
- ($C|_$C) cmd=$c; break;;
- ($C*) [ $cmd ] && die ambiguous command $C || cmd=$c;;
- esac
-done
-while getopts :0123456789A:bB:F:hl:m:MnNOsvV:x: opt # Parse command options
-do
- case $opt in
- ([0-9]) Opt_N=$Opt_N$opt;;
- ([bhlnMNOsv]) eval Opt$opt=\${Opt$opt}$opt ;;
- ([ABFmV]) eval Opt$opt=\$OPTARG ;;
- (x) Optx="$Optx --exclude=$OPTARG" ;;
- (*) Opth=1; $cmd; exit 1;;
- esac
-done
-shift $(($OPTIND - 1))
-trap "rm -rf /tmp/dv.$$" EXIT
-[ "$cmd" ] || die "no command \"$C\"" && $cmd "$@"
+$cmd "$@"
+
+# Todo:
+# * a function to squash a branch in the trunk
+# * merge another ancestor (use anc0, anc1, ...)
+# * vendor management
+# * show graphs (dot)
+# * sync: dvlib sync / mirror: resolve conflict by branching
+# * rename version.
+# * .dvignore
+# * make dvlib versions immutable.
diff --git a/bin/fv b/bin/fv
new file mode 100755
index 0000000..1f54bdc
--- /dev/null
+++ b/bin/fv
@@ -0,0 +1,6 @@
+#!/bin/sh
+# quick file viewer
+#exec fzf --multi --preview-window=right:66% --preview 'cat {1}'
+exec fzf --ansi --multi --preview-window=right:75% \
+ --bind=left:preview-page-up --bind=right:preview-page-down \
+ --preview 'bat --color always {1}'
diff --git a/bin/gauth b/bin/gauth
index 575d6fc..2da5ee2 100755
--- a/bin/gauth
+++ b/bin/gauth
@@ -1,8 +1,9 @@
#!/bin/sh
# Use backup from andOTP
-gpg -qd ~/.otp_accounts.json.gpg |
+#cat ~/.otp_accounts.json |
+gpg -qd ~/.otp_accounts.json.gpg 2>/dev/null |
jq -r '.[] | "\(.label) \(.secret)"' |
while read -r l s; do
echo "$l $(oathtool --totp -b "$s")"
-done
+done | column -t
diff --git a/bin/gemini b/bin/gemini
new file mode 100755
index 0000000..1bc3d57
--- /dev/null
+++ b/bin/gemini
@@ -0,0 +1,132 @@
+#!/usr/bin/env yaegi
+package main
+
+import (
+ "bufio"
+ "crypto/tls"
+ "fmt"
+ "io/ioutil"
+ "net/url"
+ "os"
+ "strconv"
+ "strings"
+)
+
+func main() {
+ stdinReader := bufio.NewReader(os.Stdin)
+ var u string // URL
+ links := make([]string, 0, 100)
+ history := make([]string, 0, 100)
+ for {
+ fmt.Print("> ")
+ cmd, _ := stdinReader.ReadString('\n')
+ cmd = strings.TrimSpace(cmd)
+ // Command dispatch
+ switch strings.ToLower(cmd) {
+ case "": // Nothing
+ continue
+ case "q": // Quit
+ fmt.Println("Bye!")
+ os.Exit(0)
+ case "b": // Back
+ if len(history) < 2 {
+ fmt.Println("No history yet!")
+ continue
+ }
+ u = history[len(history)-2]
+ history = history[0 : len(history)-2]
+ default:
+ index, err := strconv.Atoi(cmd)
+ if err != nil {
+ // Treat this as a URL
+ u = cmd
+ if !strings.HasPrefix(u, "gemini://") {
+ u = "gemini://" + u
+ }
+ } else {
+ // Treat this as a menu lookup
+ u = links[index-1]
+ }
+ }
+ // Parse URL
+ parsed, err := url.Parse(u)
+ if err != nil {
+ fmt.Println("Error parsing URL!")
+ continue
+ }
+ // Connect to server
+ conn, err := tls.Dial("tcp", parsed.Host+":1965", &tls.Config{InsecureSkipVerify: true})
+ if err != nil {
+ fmt.Println("Failed to connect: " + err.Error())
+ continue
+ }
+ defer conn.Close()
+ // Send request
+ conn.Write([]byte(u + "\r\n"))
+ // Receive and parse response header
+ reader := bufio.NewReader(conn)
+ responseHeader, err := reader.ReadString('\n')
+ parts := strings.Fields(responseHeader)
+ status, err := strconv.Atoi(parts[0][0:1])
+ meta := parts[1]
+ // Switch on status code
+ switch status {
+ case 1, 3, 6:
+ // No input, redirects or client certs
+ fmt.Println("Unsupported feature!")
+ case 2:
+ // Successful transaction
+ // text/* content only
+ if !strings.HasPrefix(meta, "text/") {
+ fmt.Println("Unsupported type " + meta)
+ continue
+ }
+ // Read everything
+ bodyBytes, err := ioutil.ReadAll(reader)
+ if err != nil {
+ fmt.Println("Error reading body")
+ continue
+ }
+ body := string(bodyBytes)
+ if meta == "text/gemini" {
+ // Handle Gemini map
+ links = make([]string, 0, 100)
+ preformatted := false
+ for _, line := range strings.Split(body, "\n") {
+ if strings.HasPrefix(line, "```") {
+ preformatted = !preformatted
+ } else if preformatted {
+ fmt.Println(line)
+ } else if strings.HasPrefix(line, "=>") {
+ line = line[2:]
+ bits := strings.Fields(line)
+ parsedLink, err := url.Parse(bits[0])
+ if err != nil {
+ continue
+ }
+ link := parsed.ResolveReference(parsedLink).String()
+ var label string
+ if len(bits) == 1 {
+ label = link
+ } else {
+ label = strings.Join(bits[1:], " ")
+ }
+ links = append(links, link)
+ fmt.Printf("[%d] %s\n", len(links), label)
+ } else {
+ // This should really be wrapped, but there's
+ // no easy support for this in Go's standard
+ // library
+ fmt.Println(line)
+ }
+ }
+ } else {
+ // Just print any other kind of text
+ fmt.Print(body)
+ }
+ history = append(history, u)
+ case 4, 5:
+ fmt.Println("ERROR: " + meta)
+ }
+ }
+}
diff --git a/bin/hdmi b/bin/hdmi
index d6f206a..0a81716 100755
--- a/bin/hdmi
+++ b/bin/hdmi
@@ -1,7 +1,7 @@
#!/bin/sh
# Toggle auxiliary HDMI screen on/off when connected/disconnected.
-opt="--auto --left-of eDP1"
+opt="--auto --left-of eDP-1"
#opt="--auto --right-of eDP-1"
#opt="--auto --above eDP-1"
@@ -11,5 +11,5 @@ opt="--auto --left-of eDP1"
# wait for the screen to settle
sleep 3
-xrandr | grep -q 'HDMI1 connected' || opt="--off"
-xrandr --output HDMI1 $opt
+xrandr | grep -q '^DP-1 connected' || opt="--off"
+xrandr --output DP-1 $opt
diff --git a/bin/icat b/bin/icat
index 0170513..d363e09 100755
--- a/bin/icat
+++ b/bin/icat
@@ -2,6 +2,8 @@
# Display images directly in terminal.
# Tested with xterm v361. Depends on imagemagick convert(1).
+[ "$LC_TERMINAL" = iTerm2 ] && [ -x "$HOME/.iterm2/imgcat" ] && exec "$HOME/.iterm2/imgcat" "$@"
+
# maxsize prints the geomtry size of terminal window, with
# a maximum value of 1000 pixels for width and height, or
# 640x480 if terminal size can not be probed.
diff --git a/bin/idot b/bin/idot
deleted file mode 100755
index 44387e8..0000000
--- a/bin/idot
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh
-# dot -Gsize=9 -Nshape=box -Nstyle=rounded -Tsvg | convert -- - sixel:/dev/tty
-# dot -Gsize=9 -Nfontname=Helvetica -Tsvg | convert -- - sixel:/-dev/tty
-dot -Gsize=9 -Nfontname=Arial -Tpng | icat - /dev/tty
diff --git a/bin/lt b/bin/lt
index f09f3b7..2a182f7 100755
--- a/bin/lt
+++ b/bin/lt
@@ -36,6 +36,7 @@ ca() {
pp() {
pp_s=$1 pp_b=$2 R1=''
while true; do
+ ! [ "$pp_s" ] && R1='' && return
[ "$pp_b" = "$pp_s" ] && break
pp_s=${pp_s%/*}
[ "$R1" ] && R1=$R1/.. || R1=..
diff --git a/bin/md b/bin/md
new file mode 100755
index 0000000..ca08090
--- /dev/null
+++ b/bin/md
@@ -0,0 +1,136 @@
+#!/bin/sh
+
+# See https://spec.commonmark.org for reference
+md2html() {
+ awk '
+ function newblock(nb) {
+ if (text) print "<" block ">" text "</" block ">"
+ text = ""
+ block = nb ? nb : "p"
+ }
+
+ function subinline(tgl, inl) {
+ while (match($0, tgl)) {
+ if (inline[ni] == inl)
+ ni -= sub(tgl, "</" inl ">")
+ else if (sub(tgl, "<" inl ">"))
+ inline[++ni] = inl
+ }
+ }
+
+ function dolink(href, lnk) {
+ # Undo escaped html in uris
+ gsub(/&amp;/, "\\&", href)
+ gsub(/&lt;/, "<", href)
+ gsub(/&gt;/, ">", href)
+ # & can be tricky, and not standard:
+ gsub(/&/, "\\\\\\&", href)
+ gsub(/&/, "\\\\\\&", lnk)
+ return "<a href=\"" href "\">" lnk "</a>"
+ }
+
+ BEGIN { ni = 0; nl = 0; text = ""; block = "p" }
+
+ # Escape html
+ esc != "false" { gsub("&", "\\&amp;"); gsub("<", "\\&lt;"); gsub(">", "\\&gt;") }
+
+ # Horizontal rules (_ is not in markdown)
+ /^[ ]*([-*_] ?)+[ ]*$/ && text == "" { print "<hr>"; next }
+
+ # Tables (not in markdown)
+ # Syntax:
+ # Right Align| Center Align |Left Align
+ /([ ]\|)|(\|[ ])/ {
+ if (block != "table") newblock("table")
+ nc = split($0, cells, "|")
+ $0 = "<tr>\n"
+ for (i = 1; i <= nc; i++) {
+ align = "left"
+ if (sub(/^[ ]+/, "", cells[i])) {
+ if (sub(/[ ]+$/, "", cells[i]))
+ align = "center"
+ else
+ align = "right"
+ }
+ sub(/[ ]+$/, "", cells[i])
+ $0 = $0 "<td align=\"" align "\">" cells[i] "</td>\n"
+ }
+ $0 = $0 "</tr>"
+ }
+
+ # Ordered and unordered (possibly nested) lists
+ /^[ ]*([*+-]|(([0-9]+[\.-]?)+))[ ]/ {
+ newblock("li")
+ nnl = 1
+ while (match($0, /^[ ]/)) { sub(/^[ ]/,""); nnl++ }
+ while (nl > nnl) print "</" list[nl--] ">"
+ while (nl < nnl) {
+ list[++nl] = "ol"
+ if (match($0, /^[*+-]/)) list[nl] = "ul"
+ print "<" list[nl] ">"
+ }
+ sub(/^([*+-]|(([0-9]+[\.-]?)+))[ ]/,"")
+ }
+
+ # Multi line list items
+ block == "li" { sub(/^( *)|( *)/,"") }
+
+ # Code blocks
+ /^( | )/ {
+ if (block != "code") newblock("code")
+ sub(/^( | )/, "")
+ text = text $0 "\n"
+ next
+ }
+
+ # Paragraph
+ /^$/ { newblock(); while(nl > 0) print "</" list[nl--] ">" }
+
+ # Setex-style Headers (plus h3 with underscores)
+ /^=+$/ { block = "h" 1; next }
+ /^-+$/ { block = "h" 2; next }
+ /^_+$/ { block = "h" 3; next }
+
+ # Atx-style headers
+ /^#/ {
+ newblock()
+ match($0, /#+/)
+ n = RLENGTH
+ if (n > 6) n = 6
+ sub(/# */, "#")
+ text = substr($0, RLENGTH + 1)
+ block = "h" n
+ next
+ }
+
+ {
+ # Images
+ while (match($0, /!\[[^\]]+\]\([^\)]+\)/)) {
+ split(substr($0, RSTART, RLENGTH), a, /(!\[)|\)|(\]\()/)
+ sub(/!\[[^\]]+\]\([^\)]+\)/, "<img src=\"" a[3] "\" alt=\"" a[2] "\">")
+ }
+ # Links
+ while (match($0, /\[[^\]]+\]\([^\)]+\)/)) {
+ split(substr($0, RSTART, RLENGTH), a, /[\[\)]|(\]\()/)
+ sub(/\[[^\]]+\]\([^\)]+\)/, dolink(a[3], a[2]))
+ }
+ # Auto links (uri matching is poor)
+ na = split($0, a, /(^\()|[ ]|([,\.\)]([ ]|$))/)
+ for (i = 1; i <= na; i++)
+ if (match(a[i], /^(((https?|ftp|file|news|irc):\/\/)|(mailto:)).+$/))
+ sub(a[i], dolink(a[i], a[i]))
+ # Inline
+ subinline("(\\*\\*)|(__)", "strong")
+ subinline("\\*", "em")
+ subinline("`", "code")
+ text = text (text ? " " : "") $0
+ }
+
+ END {
+ while(ni > 0) text = text "</" inline[ni--] ">"
+ newblock()
+ while(nl > 0) print "</" list[nl--] ">"
+ }' "$1"
+}
+
+md2html "$1"
diff --git a/bin/md2html b/bin/md2html
index 35e1d72..7be4aaa 100755
--- a/bin/md2html
+++ b/bin/md2html
@@ -1,43 +1,42 @@
#!/usr/bin/awk -f
# md2html.awk
-# by: Jesus Galan (yiyus) <yiyu.jgl@gmail>, May 2009
# Usage:
# md2html file.md > file.html
# Options: -v esc=false to not escape html
-function newblock(nblock){
- if(text)
- print "<" block ">" text "</" block ">";
- text = "";
- block = nblock ? nblock : "p";
+function newblock(nblock) {
+ if (text)
+ print "<" block ">" text "</" block ">"
+ text = ""
+ block = nblock ? nblock : "p"
}
-function subinline(tgl, inl){
- while(match($0, tgl)){
+function subinline(tgl, inl) {
+ while (match($0, tgl)){
if (inline[ni] == inl)
- ni -= sub(tgl, "</" inl ">");
+ ni -= sub(tgl, "</" inl ">")
else if (sub(tgl, "<" inl ">"))
- inline[++ni] = inl;
+ inline[++ni] = inl
}
}
-function dolink(href, lnk){
+function dolink(href, lnk) {
# Undo escaped html in uris
- gsub(/&amp;/, "\\&", href);
- gsub(/&lt;/, "<", href);
- gsub(/&gt;/, ">", href);
+ gsub(/&amp;/, "\\&", href)
+ gsub(/&lt;/, "<", href)
+ gsub(/&gt;/, ">", href)
# & can be tricky, and not standard:
- gsub(/&/, "\\\\\\&", href);
- gsub(/&/, "\\\\\\&", lnk);
- return "<a href=\"" href "\">" lnk "</a>";
+ gsub(/&/, "\\\\\\&", href)
+ gsub(/&/, "\\\\\\&", lnk)
+ return "<a href=\"" href "\">" lnk "</a>"
}
BEGIN {
ni = 0; # inlines
nl = 0; # nested lists
- text = "";
- block = "p";
+ text = ""
+ block = "p"
}
# Escape html
@@ -49,128 +48,122 @@ esc != "false" {
# Horizontal rules (_ is not in markdown)
/^[ ]*([-*_] ?)+[ ]*$/ && text == "" {
- print "<hr>";
- next;
+ print "<hr>"
+ next
}
# Tables (not in markdown)
# Syntax:
# Right Align| Center Align |Left Align
/([ ]\|)|(\|[ ])/ {
- if(block != "table")
- newblock("table");
- nc = split($0, cells, "|");
- $0 = "<tr>\n";
- for(i = 1; i <= nc; i++){
- align = "left";
- if(sub(/^[ ]+/, "", cells[i])){
- if(sub(/[ ]+$/, "", cells[i]))
- align = "center";
+ if (block != "table")
+ newblock("table")
+ nc = split($0, cells, "|")
+ $0 = "<tr>\n"
+ for (i = 1; i <= nc; i++){
+ align = "left"
+ if (sub(/^[ ]+/, "", cells[i])){
+ if (sub(/[ ]+$/, "", cells[i]))
+ align = "center"
else
- align = "right";
+ align = "right"
}
- sub(/[ ]+$/,"", cells[i]);
- $0 = $0 "<td align=\"" align "\">" cells[i] "</td>\n";
+ sub(/[ ]+$/,"", cells[i])
+ $0 = $0 "<td align=\"" align "\">" cells[i] "</td>\n"
}
- $0 = $0 "</tr>";
+ $0 = $0 "</tr>"
}
# Ordered and unordered (possibly nested) lists
/^[ ]*([*+-]|(([0-9]+[\.-]?)+))[ ]/ {
- newblock("li");
- nnl = 1;
- while(match($0, /^[ ]/)){
- sub(/^[ ]/,"");
- nnl++;
+ newblock("li")
+ nnl = 1
+ while (match($0, /^[ ]/)){
+ sub(/^[ ]/,"")
+ nnl++
}
- while(nl > nnl)
- print "</" list[nl--] ">";
- while(nl < nnl){
- list[++nl] = "ol";
- if(match($0, /^[*+-]/))
- list[nl] = "ul";
- print "<" list[nl] ">";
+ while (nl > nnl)
+ print "</" list[nl--] ">"
+ while (nl < nnl){
+ list[++nl] = "ol"
+ if (match($0, /^[*+-]/))
+ list[nl] = "ul"
+ print "<" list[nl] ">"
}
- sub(/^([*+-]|(([0-9]+[\.-]?)+))[ ]/,"");
+ sub(/^([*+-]|(([0-9]+[\.-]?)+))[ ]/,"")
}
# Multi line list items
block == "li" {
- sub(/^( *)|( *)/,"");
+ sub(/^( *)|( *)/,"")
}
# Code blocks
/^( | )/ {
- if(block != "code")
- newblock("code");
- sub(/^( | )/, "");
- text = text $0 "\n";
- next;
+ if (block != "code")
+ newblock("code")
+ sub(/^( | )/, "")
+ text = text $0 "\n"
+ next
}
-# Paragraph
+# Paragraphs
/^$/ {
- newblock();
- while(nl > 0)
- print "</" list[nl--] ">";
+ newblock()
+ while (nl > 0)
+ print "</" list[nl--] ">"
}
-# Setex-style Headers
-# (Plus h3 with underscores.)
-/^=+$/ {
- block = "h" 1;
- next;
-}
-
-/^-+$/ {
- block = "h" 2;
- next;
+# Headers
+/^#/ {
+ newblock()
+ match($0, /#+/)
+ n = RLENGTH
+ if (n > 6)
+ n = 6
+ text = substr($0, RLENGTH + 1)
+ block = "h" n
+ next
}
-/^_+$/ {
- block = "h" 3;
- next;
+# Alternate headers (underlined)
+/^=+$/ {
+ block = "h" 1
+ next
}
-# Atx-style headers
-/^#/ {
- newblock();
- match($0, /#+/);
- n = RLENGTH;
- if(n > 6)
- n = 6;
- text = substr($0, RLENGTH + 1);
- block = "h" n;
- next;
+/^-+$/ {
+ block = "h" 2
+ next
}
-// {
+{
# Images
- while(match($0, /!\[[^\]]+\]\([^\)]+\)/)){
- split(substr($0, RSTART, RLENGTH), a, /(!\[)|\)|(\]\()/);
- sub(/!\[[^\]]+\]\([^\)]+\)/, "<img src=\"" a[3] "\" alt=\"" a[2] "\">");
+ while (match($0, /!\[[^\]]+\]\([^\)]+\)/)){
+ split(substr($0, RSTART, RLENGTH), a, /(!\[)|\)|(\]\()/)
+ sub(/!\[[^\]]+\]\([^\)]+\)/, "<img src=\"" a[3] "\" alt=\"" a[2] "\">")
}
# Links
- while(match($0, /\[[^\]]+\]\([^\)]+\)/)){
- split(substr($0, RSTART, RLENGTH), a, /[\[\)]|(\]\()/);
- sub(/\[[^\]]+\]\([^\)]+\)/, dolink(a[3], a[2]));
+ while (match($0, /\[[^\]]+\]\([^\)]+\)/)){
+ split(substr($0, RSTART, RLENGTH), a, /[\[\)]|(\]\()/)
+ sub(/\[[^\]]+\]\([^\)]+\)/, dolink(a[3], a[2]))
}
# Auto links (uri matching is poor)
- na = split($0, a, /(^\()|[ ]|([,\.\)]([ ]|$))/);
- for(i = 1; i <= na; i++)
- if(match(a[i], /^(((https?|ftp|file|news|irc):\/\/)|(mailto:)).+$/))
- sub(a[i], dolink(a[i], a[i]));
+ na = split($0, a, /(^\()|[ ]|([,\.\)]([ ]|$))/)
+ for (i = 1; i <= na; i++)
+ if (match(a[i], /^(((https?|ftp|file|news|irc):\/\/)|(mailto:)).+$/))
+ sub(a[i], dolink(a[i], a[i]))
# Inline
- subinline("(\\*\\*)|(__)", "strong");
- subinline("\\*", "em");
- subinline("`", "code");
- text = text (text ? " " : "") $0;
+ subinline("(\\*\\*)|(__)", "strong")
+ subinline("\\*", "em")
+ subinline("`", "code")
+ text = text (text ? " " : "") $0
}
END {
- while(ni > 0)
- text = text "</" inline[ni--] ">";
- newblock();
- while(nl > 0)
- print "</" list[nl--] ">";
+ while (ni > 0)
+ text = text "</" inline[ni--] ">"
+ newblock()
+ while (nl > 0)
+ print "</" list[nl--] ">"
}
diff --git a/bin/migrate_vimki b/bin/migrate_vimki
new file mode 100755
index 0000000..15242e6
--- /dev/null
+++ b/bin/migrate_vimki
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+# Migrate Wiki content from vimki to vimki2 format.
+
+fixfile() {
+ awk '
+ {
+ for (i = 1; i <= NR; i++) {
+ if (match($i, /^[A-Z][A-Z_0-9]*[a-z][a-z0-9]*[_A-Z]/) == 0) continue
+ s = $i
+ l = ""
+ while (match(s, /^[A-Z][A-Z_0-9]*[a-z]+/) != 0) {
+ l = l substr(s, 1, RLENGTH) " "
+ s = substr(s, RLENGTH+1)
+ }
+ p = match(s, /[^A-Za-z0-9_]/)
+ if (p != 0) {
+ s2 = substr(s, 1, p-1) "]" substr(s, p)
+ s = s2
+ } else s = s "]"
+ l = "[" l s
+ gsub(/[ \t_]+/, " ", l)
+ sub(/ *]/, "]", l)
+ $i = tolower(l)
+ }
+ print
+ }
+ ' "${1:-HomePage}"
+}
+
+fixname() {
+ echo "$1" | awk '{
+ s = $0
+ l = ""
+ while (match(s, /^[A-Z][A-Z_0-9]*[a-z]+/) != 0) {
+ l = l substr(s, 1, RLENGTH) " "
+ s = substr(s, RLENGTH+1)
+ }
+ p = match(s, /[^A-Za-z0-9_]/)
+ if (p != 0) {
+ s2 = substr(s, 1, p-1) "]" substr(s, p)
+ s = s2
+ }
+ l = l s
+ gsub(/[ \t_]+/, " ", l)
+ sub(/ *$/, "", l)
+ print tolower(l)
+
+ }'
+}
+
+mkdir -p $HOME/Wiki2
+cd $HOME/Wiki
+for file in *; do
+ new=$(fixname "$file")
+ echo "$new"
+ fixfile "$file" > "$HOME/Wiki2/$new"
+done
diff --git a/bin/open b/bin/open
deleted file mode 100755
index 0fa97a7..0000000
--- a/bin/open
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/bin/sh
-
-exec >/tmp/open.out 2>&1
-set -x
-
-cmd=xdg-open
-case $1 in
-(*.mkv|*.mp4) cmd=vlc;;
-esac
-
-exec $cmd "$@"
diff --git a/bin/p4a b/bin/p4a
new file mode 100755
index 0000000..30e54ff
--- /dev/null
+++ b/bin/p4a
@@ -0,0 +1,3 @@
+#!/bin/sh
+adb forward tcp:8222 tcp:8022
+ssh ssh://u0_a250@localhost:8222
diff --git a/bin/rdate b/bin/rdate
new file mode 100755
index 0000000..a69d137
--- /dev/null
+++ b/bin/rdate
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+# Set date on remote machine from local one. This version works
+# from MacOS to AlpineLinux. It is intended to re-sync clock of a
+# virtual machine after host wakeup.
+
+# TODO: option to display the time difference between host and remote.
+# hint: use 'date +%s' to get timestamp in seconds since epoch.
+
+ssh "$1" "sudo date $(date +%m%d%H%M%Y.%S)"
+
diff --git a/bin/start_godoc b/bin/start_godoc
new file mode 100755
index 0000000..c6a716b
--- /dev/null
+++ b/bin/start_godoc
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+# Start a godoc server on port 6060
+
+exec /home/marc/go/bin/godoc >/tmp/godoc.out 2>&1 &
diff --git a/bin/status2 b/bin/status2
new file mode 100755
index 0000000..1b7ea55
--- /dev/null
+++ b/bin/status2
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+ip() {
+ while read -r w1 w2 w3; do
+ [ $w1 = /32 ] && [ $w2 = host ] && [ $ip != 127.0.0.1 ] && break
+ ip=$w2
+ done < /proc/net/fib_trie
+}
+
+power() {
+ while read -r line; do
+ case $line in POWER_SUPPLY_CAPACITY*) power=${line#*=}% && break;; esac
+ done < /sys/class/power_supply/BAT0/uevent
+}
+
+status() {
+ power
+ ip
+ sound="$(pamixer --get-volume-human)"
+ clock="$(date +'%a %d %b %H:%M')"
+}
+
+while true; do
+ status && echo "$ip , power $power , sound $sound , $clock"
+ sleep 17
+done |
+root-tail -g '500x20-80-0' -fn '-*-fixed-medium-*-*-*-15-*-*-*-*-*-*-*' --color grey70 -
diff --git a/bin/update_hosts b/bin/update_hosts
new file mode 100755
index 0000000..74bf25b
--- /dev/null
+++ b/bin/update_hosts
@@ -0,0 +1,28 @@
+#!/bin/sh -e
+
+# Update /etc/hosts with a well curated blacklist of malware, ads, porn, etc.
+# Custom hosts are preserved.
+
+[ "$USER" = root ] || exec sudo "$0" "$@"
+
+echo 'Checking from https://github.com/StevenBlack/hosts:'
+lsd=$(curl -s 'https://api.github.com/repos/StevenBlack/hosts/commits?path=hosts&page=1&per_page=1' | jq -r '.[0].commit.committer.date')
+#echo "last source update: $(date -j -f "%FT%TZ" "$lsd")"
+echo "last source update: $(date --date="$lsd")"
+echo "last local update: $(date -r /etc/hosts)"
+# [ $(date -j -f "%FT%TZ" "$lsd" +%s) -lt $(date -r /etc/hosts +%s) ] && echo 'Nothing to do' && exit
+[ $(date --date="$lsd" +%s) -lt $(date -r /etc/hosts +%s) ] && echo 'Nothing to do' && exit
+
+cd /etc
+cp -p hosts hosts.old
+hosts=$(awk '/^# Custom host/, /^# End of custom host/' hosts.old)
+curl -s 'https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts' | awk '
+ BEGIN { hosts = ARGV[1]; ARGV[1] = "" }
+ /^# Custom host/ { print hosts; next }
+ /^# End of custom host/ { next }
+ { print }
+' "$hosts" > hosts.new
+
+mv hosts.new hosts
+[ "$(uname -s)" != Darwin ] || { dscacheutil -flushcache; killall -HUP mDNSResponder; }
+diff -u /etc/hosts.old /etc/hosts | diffstat
diff --git a/bin/update_kernel_host b/bin/update_kernel_host
new file mode 100755
index 0000000..4f2cbbf
--- /dev/null
+++ b/bin/update_kernel_host
@@ -0,0 +1,13 @@
+#!/bin/sh -ex
+
+# After 'apk upgrade', update kernel and initrd on VM host
+
+if [ -f /etc/arch-release ]; then
+ scp /boot/Image /boot/initramfs-linux.img marc@m1:.vm/a1
+ exit
+fi
+
+sudo cp /boot/initramfs-virt /tmp
+sudo chmod a+r /tmp/initramfs-virt
+gunzip < /boot/vmlinuz-virt > /tmp/vmlinux
+scp /tmp/initramfs-virt /tmp/vmlinux marc@m1:.vm/vm1/
diff --git a/bin/vcat b/bin/vcat
new file mode 100755
index 0000000..7c162d4
--- /dev/null
+++ b/bin/vcat
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+# Use vim to cat a file.
+# Useful if file is vim-encrypted (password silently expected).
+
+trap "stty '$(stty -g)'" EXIT
+stty -echo
+vim -es '+%p' '+:q!' "$1"
diff --git a/bin/vimki b/bin/vimki
new file mode 100755
index 0000000..e3e0f4d
--- /dev/null
+++ b/bin/vimki
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+# Batch operations on vimki
+
+# rename changes the link name and propagates the change in the wiki
+rename1() {
+ [ "$(head -c 8 "$3")" = VimCrypt ] && return
+ gawk -v old="$1" -v new="$2" -v IGNORECASE=1 '
+ {
+ gsub("[[]" old "[]]", "[" new "]")
+ print
+ }
+ ' "$3" > "$3.$$" && mv "$3.$$" "$3"
+}
+
+link2rx() {
+ gawk -v rx="$1" -v IGNORECASE=1 '
+ BEGIN {
+ gsub(/_/, "\\W+", rx)
+ gsub(/a/, "[aåäáàâã]", rx)
+ gsub(/c/, "[cç]", rx)
+ gsub(/e/, "[eéèêë]", rx)
+ gsub(/i/, "[iîíìï]", rx)
+ gsub(/o/, "[oôöóõò]", rx)
+ gsub(/u/, "[uùûüú]", rx)
+ gsub(/y/, "[yÿ]", rx)
+ gsub(/n/, "[nñ]", rx)
+ print rx
+ }'
+}
+
+#shellcheck disable=SC2020
+
+linkfile() {
+ echo "$1" |
+ tr '[:upper:]' '[:lower:]' |
+ tr 'åäáàâãçéèêëîíìïôöóõòùûüúÿñ' 'aaaaaaceeeeiiiiooooouuuuyn' |
+ awk '{gsub(/\W+/, "_"); print}'
+}
+
+rename() {
+ rx=$(link2rx "$1")
+ # Parallelize renaming
+ echo *.md | xargs -n 1 -P 8 "$0" rename1 "$rx" "$2"
+ oldf="$(linkfile "$1").md"
+ [ -f "$oldf" ] && mv "$oldf" "$(linkfile "$2").md"
+}
+
+fixfiles() {
+ for f in *; do
+ mv "$f" "$(linkfile "$f")"
+ done
+}
+
+cd ~/Wiki || exit
+"$@"
diff --git a/bin/vm b/bin/vm
new file mode 100755
index 0000000..35a107e
--- /dev/null
+++ b/bin/vm
@@ -0,0 +1,286 @@
+#!/bin/sh
+
+# Manage virtual machines
+#
+# Prereq:
+# - curl
+# - cdrtools (isoinfo)
+# - expect
+# - qemu
+# - screen
+#
+# TODO:
+# - extract bzImage and initrd.gz with isoinfo
+# - ssh scripts to finish install (once ssh is ready)
+#
+# DONE:
+# - setup vde and networking
+# - setup ssh keys so it is possible to ssh in vm from host
+#
+unset CDPATH
+export LC_ALL=C IFS='
+'
+
+version='vm-0.1'
+
+arch=$(uname -m)
+sys=$(uname -s)
+alpine_version='3.15.0'
+pubkey=$HOME/.ssh/id_rsa.pub
+dir="${VM_DIR:-$HOME/.vm}"
+
+console() {
+ usage 'console name' 'Attach a console to a virtual machine' && return
+ [ "$1" ] || die "missing argument"
+ is_running "$1" && screen -r "vm!$1!"
+}
+
+create_arch() {
+ usage 'create_arch [-s size] name' 'Create an archlinux disk image' && return
+ size=8g
+ while getopts :s: opt; do
+ case $opt in
+ s) size=$OPTARG ;;
+ *) Opth=2 create_arch; return ;;
+ esac
+ done
+ shift $((OPTIND - 1))
+ [ -d "$dir/$1" ] && die "create failed: $dir/$1 already exists"
+ mkdir "$dir/$1"
+ cd "$dir/$1" || die "create failed: invalid directory $dir/$1"
+ qemu-img create "$1.raw" "$size" || die "create failed"
+ mac=$(new_macaddr)
+ ip=$(new_ip)
+ hdd="$1.raw"
+ echo "hdd=$hdd
+mac=$mac
+ip=$ip" >> config
+
+ mkfs.ext4 "$1.raw"
+ mkdir -p mnt
+ sudo mount "$1.raw" mnt
+ sudo pacstrap mnt base base-devel
+ sudo umount mnt
+}
+
+create() {
+ usage 'create [-s size] name' 'Create an alpinelinux disk image' && return
+ size=8g
+ while getopts :s: opt; do
+ case $opt in
+ s) size=$OPTARG ;;
+ *) Opth=2 create_arch; return ;;
+ esac
+ done
+ shift $((OPTIND - 1))
+ [ -d "$dir/$1" ] && die "create failed: $dir/$1 already exists"
+ mkdir "$dir/$1"
+ cd "$dir/$1" || die "create failed: invalid directory $dir/$1"
+ qemu-img create "$1.raw" "$size" || die "create failed"
+ mac=$(new_macaddr)
+ ip=$(new_ip)
+ hdd="$1.raw"
+ echo "hdd=$hdd
+mac=$mac
+ip=$ip" >> config
+
+ # Before install do not use virtio, as devices may not recognized as bootable
+ # TODO: alternate way: extract kernel and initrd files and pass them directly to qemu
+ screen -S "vm!$1!" -d -m qemu-system-$arch -nographic \
+ -cdrom ../alpine-iso/alpine-virt-$alpine_version-$arch.iso \
+ -hdd "$hdd" -net nic,macaddr=$mac -net vde
+ setup_alpine "$1"
+ post_setup_alpine "$1"
+}
+
+die() { [ "$1" ] && echo "$0: $*" >&2; exit 1; }
+
+help() {
+ usage 'help' 'Print this help text' && return
+ printf "$version\n Manage virtual machines\n\nUsage: vm command [options] [args]\n"
+ Opth=1; for c in $Cmdlist; do $c; done
+}
+
+init() {
+ mkdir -p "$dir"
+}
+
+init_alpine_iso() {
+ usage init_alpine_iso '' && return
+ mkdir -p "$dir/alpine-iso"
+ iso_url="https://dl-cdn.alpinelinux.org/alpine/v${alpine_version%.*}/releases/$arch"
+ iso="alpine-virt-$alpine_version-$arch.iso"
+ cd "$dir/alpine-iso"
+ [ -f "$iso" ] || curl -LO "$iso_url/$iso" || rm -f "$iso"
+ echo "iso=$iso
+" > config
+ echo 10 > ../index
+}
+
+is_running() { screen -ls "vm!$1!" >/dev/null 2>&1; }
+
+ls() {
+ usage 'ls' 'list virtual machines' && return
+ init && cd "$dir" || die "could not change dir to $dir"
+ for i in */; do
+ i=${i%/}
+ [ "$i" = '*' ] && continue
+ is_running "$i" && state=active || state=stopped
+ printf "%-20s %s\n" "$i" "$state"
+ done
+}
+
+new_ip() {
+ read index < $dir/index
+ index=$((index + 1))
+ echo "$index" > $dir/index
+ echo "10.0.2.$index/24"
+}
+
+new_macaddr() { printf 'de:ad:be:ef:%02x:%02x\n' $((RANDOM % 256)) $((RANDOM % 256)); }
+
+pidof() {
+ usage 'pidof name' 'print the PID of a virtual machine' && return
+ p=$(screen -ls "vm!$1!" | awk 'NR==2 {print substr($1, 1, index($1, ".")-1)}')
+ [ "$p" ] && pgrep -P $p
+}
+
+post_setup_alpine() {
+ start "$1"
+ read proto key id < "$pubkey"
+ # echo '
+ expect -c '
+ set timeout -1
+ spawn screen -x "vm!'$1'!"
+ expect {
+ " login: " { send "root\r"; exp_continue }
+ "Password: " { send "root\r"; exp_continue }
+ ":~# "
+ }
+ send "mkdir -pm 0700 .ssh\r"
+ expect ":~# "
+ send "echo '$proto' '$key' '$id' >.ssh/authorized_keys\r"
+ expect ":~# "
+ send "exit\r"
+ '
+}
+
+# setup_alpine automates alpine installation from iso to image.
+# When done, base image system is ready, with storage and network up.
+# No user nor ssh access configured yet.
+# TODO: custom network (in case of no dhcp).
+setup_alpine() {
+ usage 'setup_alpine' && return
+ expect -c '
+ set timeout -1
+ spawn screen -x "vm!'$1'!"
+ expect {
+ "localhost login: " { send "root\r"; exp_continue }
+ "localhost:~# " { send "SWAP_SIZE=0 setup-alpine\r"; exp_continue }
+ "Select keyboard layout: " { send "\r"; exp_continue }
+ "Enter system hostname" { send "'$1'\r"; exp_continue }
+ "Which one do you want to initialize?" { send "\r"; exp_continue }
+ "Ip address for eth0?" { send "'$ip'\r"; exp_continue }
+ "Gateway?" { send "10.0.2.2\r"; exp_continue }
+ "manual network configuration?" { send "\r"; exp_continue }
+ "DNS domain name?" { send "lan\r"; exp_continue }
+ "DNS nameserver(s)?" { send "10.0.2.2 1.1.1.1\r"; exp_continue }
+ "New password:" { send "root\r"; exp_continue }
+ "Retype password:" { send "root\r"; exp_continue }
+ "Which timezone are you in?" { send "\r"; exp_continue }
+ "HTTP/FTP proxy URL?" { send "\r"; exp_continue }
+ "Enter mirror number " { send "\r"; exp_continue }
+ "Which SSH server?" { send "\r"; exp_continue }
+ "Which disk(s) would you like to use?" { send "sda\r"; exp_continue }
+ "How would you like to use it?" { send "sys\r"; exp_continue }
+ "Erase the above disk(s) and continue?" { send "y\r"; exp_continue }
+ "Installation is complete" { send "poweroff\r" }
+ }
+ interact
+ '
+}
+
+start_vde() {
+ usage start_vde && return
+ sudo sh <<- EOT
+ vde_switch -tap tap0 -sock /tmp/vde.ctl -daemon -mod 666
+ sleep 1
+ ip address add 10.0.2.2/24 dev tap0
+ ip link set tap0 up
+ echo 1 > /proc/sys/net/ipv4/ip_forward
+ iptables -t nat -A POSTROUTING -s 10.0.2.0/24 -o eth0 -j MASQUERADE
+ iptables -t nat -A POSTROUTING -s 10.0.2.0/24 -o wlan0 -j MASQUERADE
+ EOT
+ #slirpvde --dhcp --daemon
+}
+
+stop_vde() {
+ # killall slirpvde
+ sudo sh <<- EOT
+ iptables -t nat -D POSTROUTING -s 10.0.2.0/24 -o eth0 -j MASQUERADE
+ iptables -t nat -D POSTROUTING -s 10.0.2.0/24 -o wlan0 -j MASQUERADE
+ ip link set tap0 down
+ killall vde_switch
+ EOT
+}
+
+start() {
+ usage 'start [-acd] name' 'start a virtual machine' && return
+ while getopts :acd opt; do
+ case $opt in
+ a) opta=1 ;;
+ c) boot=c ;;
+ d) boot=d ;;
+ esac
+ done
+ shift $((OPTIND -1))
+ [ "$1" ] || die 'start failed: name missing'
+ cd "$dir/$1" || die "start failed: invalid directory $dir/$1"
+ is_running "$1" || start_qemu "$1"
+ [ "$opta" ] && console "$1"
+}
+
+start_qemu() (
+ opt='-nographic -cpu max'
+ [ "$sys" = Linux ] && opt="$opt -enable-kvm"
+ . ./config || die "could not source $PWD/config"
+ exec 1>>qemu.log 2>&1
+ date +%F_%T
+ set -x
+ screen -S "vm!$1!" -d -m qemu-system-$arch $opt \
+ ${smp+-smp $smp} \
+ ${ram+-m $ram} \
+ ${mac+-net nic,macaddr=$mac,model=virtio-net-pci} -net vde \
+ ${hdd+-drive file="$hdd",if=virtio,media=disk,format=raw} \
+ ${iso+-drive file="$iso",if=virtio,media=cdrom,format=raw} \
+ ${boot+-boot $boot}
+)
+
+stop() {
+ usage 'stop name' 'stop a virtual machine' && return
+ is_running "$1" && kill "$(pidof "$1")"
+}
+
+usage() {
+ case $Opth in
+ 1) printf " %-34s %s\n" "$1" "$2" ;;
+ 2) printf "$0 $1\n\t$2\n" ;;
+ *) return 1 ;;
+ esac
+}
+
+version() {
+ usage 'version' 'Print version' && return
+ echo "$version"
+}
+
+Cmdlist='console create help init_alpine_iso ls pidof setup_alpine start start_vde stop version'
+[ "$1" ] && C=$1 && shift 1 || { help; exit 1; }
+#for c in $Cmdlist; do
+# case $c in
+# ("$C") cmd=$c; break ;;
+# ("$C"*) [ "$cmd" ] && die "ambiguous command $C" || cmd=$c ;;
+# esac
+#done
+cmd=$C
+[ "$cmd" ] || { help; exit 1; } && $cmd "$@"
diff --git a/bin/w3 b/bin/w3
new file mode 100755
index 0000000..90cc29c
--- /dev/null
+++ b/bin/w3
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+cols=$(stty -a|awk -F "[ ;]" '{print $9; exit}')
+[ $cols -gt 72 ] && stty cols 72
+w3m "${@:-https://ddg.gg/lite/}"
+stty cols "$cols"
diff --git a/bin/wag b/bin/wag
new file mode 100755
index 0000000..a1542b6
--- /dev/null
+++ b/bin/wag
@@ -0,0 +1,335 @@
+#!/bin/sh
+
+## wag is a tool to generate static web sites
+##
+## Commands:
+
+unset CDPATH
+export LC_ALL=C IFS='
+'
+
+cmd=$(command -v "$0")
+
+dest=./public
+
+footer() {
+ :
+}
+
+# front parses front matter
+front() {
+ {
+ read a b && [ "$a" = "---" ] && [ "$b" = "" ] || return
+ while read a b; do
+ [ "$a" = "---" ] && [ "$b" = "" ] && break
+ eval "front_${a%:}=\"$b\""
+ done
+ } < "$1"
+}
+
+## gen generates site content from source files
+gen() {
+ [ -d "$dest" ] || mkdir -p "$dest"
+ for f in *.md; do
+ [ "$f" = "*.md" ] && continue
+ g="$dest/${f%.md}.html"
+ [ "$f" -ot "$g" ] && continue
+ echo "f: $f $g"
+ front "$f"
+ {
+ header
+ md2html "$f"
+ footer
+ } > "$g"
+ done
+}
+
+header() {
+ cat <<- EOT
+ <title>$front_title</title>
+ <meta charset="utf-8">
+ <style>body {max-width: 52em; margin: 1em auto; padding: 1em}</style>
+ EOT
+}
+
+## help prints this program documentation
+help() { awk '/^## / {print substr($0, 4)}' "$cmd"; }
+
+lipsum='Lorem ipsum dolor sit amet, consectetur adipiscing elit,
+sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
+nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
+reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
+pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
+culpa qui officia deserunt mollit anim id est laborum.'
+
+# An overly simplified http request parser for static web sites.
+http_request() {
+ read -r cmd uri proto
+ case $uri in */) uri="${uri}index.html" ;; esac
+ while true; do
+ read -r line || break
+ [ ${#line} = 0 ] && break
+ done
+ printf 'HTTP/1.1 200 OK\n\n' && cat "${uri#/}"
+}
+
+md2h() { want="$2" got=$(echo "$1" | md2html -); }
+
+md2html() {
+ tmp=$(mktemp -u)
+ trap "rm -f '$tmp'" EXIT
+ {
+ cat "${1:--}" | tee "$tmp" | awk '/^[ ]*\[[^]]+\]:/'
+ cat "$tmp"
+ } |
+ awk '
+ function newblock(nblock) {
+ if (text)
+ print "<" block ">" text "</" block ">"
+ text = ""
+ out = 1
+ block = nblock ? nblock : "p"
+ }
+
+ function subinline(tgl, inl) {
+ while (match($0, tgl)) {
+ if (inline[ni] == inl)
+ ni -= sub(tgl, "</" inl ">")
+ else if (sub(tgl, "<" inl ">"))
+ inline[++ni] = inl
+ }
+ }
+
+ function dolink(href, lnk) {
+ # Undo escaped html in uris
+ gsub(/&amp;/, "\\&", href)
+ gsub(/&lt;/, "<", href)
+ gsub(/&gt;/, ">", href)
+ # & can be tricky, and not standard:
+ gsub(/&/, "\\\\\\&", href)
+ gsub(/&/, "\\\\\\&", lnk)
+ return "<a href=\"" href "\">" lnk "</a>"
+ }
+
+ BEGIN {
+ ni = 0 # inlines
+ nl = 0 # nested lists
+ out = 0 # 0 if no output so far
+ text = ""
+ block = "p"
+ }
+
+ # Skip front matter.
+ out == 0 && $0 == "---" {
+ do
+ getline
+ while ($0 != "---")
+ next
+ }
+
+ # Escape HTML.
+ esc != "false" {
+ gsub("&", "\\&amp;")
+ gsub("<", "\\&lt;")
+ gsub(">", "\\&gt;")
+ }
+
+ # Internal references.
+ match($0, /^[ ]*\[[^]]+\]:/) > 0 {
+ k = substr($0, RSTART+1, RLENGTH-3)
+ v = substr($0, RLENGTH+1)
+ sub(/^[ ]/, "", v)
+ sub(/[ ]$/, "", v)
+ ref[substr($0, RSTART+1, RLENGTH-3)] = v
+ next
+ }
+
+ # Horizontal rules.
+ /^[ ]*([-*_] ?)+[ ]*$/ && text == "" {
+ print "<hr>"
+ next
+ }
+
+ # Tables. Syntax:
+ # Right Align| Center Align |Left Align
+ /([ ]\|)|(\|[ ])/ {
+ if (block != "table")
+ newblock("table")
+ nc = split($0, cells, "|")
+ $0 = "<tr>\n"
+ for (i = 1; i <= nc; i++) {
+ align = "left"
+ if (sub(/^[ ]+/, "", cells[i])) {
+ if (sub(/[ ]+$/, "", cells[i]))
+ align = "center"
+ else
+ align = "right"
+ }
+ sub(/[ ]+$/, "", cells[i])
+ $0 = $0 "<td align=\"" align "\">" cells[i] "</td>\n"
+ }
+ $0 = $0 "</tr>"
+ }
+
+ # Ordered and unordered (possibly nested) lists.
+ /^[ ]*([*+-]|(([0-9]+[.-]?)+))[ ]/ {
+ newblock("li")
+ nnl = 1
+ while (match($0, /^[ ]/)) {
+ sub(/^[ ]/, "")
+ nnl++
+ }
+ while (nl > nnl)
+ print "</" list[nl--] ">"
+ while (nl < nnl) {
+ list[++nl] = "ol"
+ if (match($0, /^[*+-]/))
+ list[nl] = "ul"
+ print "<" list[nl] ">"
+ }
+ sub(/^([*+-]|(([0-9]+[.-]?)+))[ ]/, "")
+ }
+
+ # Multi line list items.
+ block == "li" {
+ sub(/^( *)|( *)/, "")
+ }
+
+ # Code blocks.
+ /^( | )/ {
+ if (block != "code")
+ newblock("code")
+ sub(/^( | )/, "")
+ text = text $0 "\n"
+ next
+ }
+
+ # Paragraphs.
+ /^$/ {
+ newblock()
+ while (nl > 0)
+ print "</" list[nl--] ">"
+ }
+
+ # Headers.
+ /^#+ / {
+ newblock()
+ match($0, /#+/)
+ n = RLENGTH
+ if (n > 6)
+ n = 6
+ text = substr($0, RLENGTH + 1)
+ sub(/^ */, "", text)
+ block = "h" n
+ next
+ }
+
+ # Alternate headers (underlined).
+ /^=+$/ {
+ block = "h" 1
+ next
+ }
+
+ /^-+$/ {
+ block = "h" 2
+ next
+ }
+
+ {
+ # Images.
+ while (match($0, /!\[[^]]+\]\([^)]+\)/)) {
+ split(substr($0, RSTART, RLENGTH), a, /(!\[)|\)|(\]\()/)
+ sub(/!\[[^]]+\]\([^)]+\)/, "<img src=\"" a[3] "\" alt=\"" a[2] "\">")
+ }
+ # Links.
+ while (match($0, /\[[^]]+\]\([^)]+\)/)) {
+ split(substr($0, RSTART, RLENGTH), a, /[[)]|(\]\()/)
+ sub(/\[[^]]+\]\([^)]+\)/, dolink(a[3], a[2]))
+ }
+ # Internal references.
+ while (match($0, /\[[^]]+\]/)) {
+ k = substr($0, RSTART+1, RLENGTH-2)
+ sub(/\[[^]]+\]/, dolink(ref[k], k))
+ }
+ # Auto links (uri matching is poor).
+ na = split($0, a, /(^\()|[ ]|([,.)]([ ]|$))/)
+ for (i = 1; i <= na; i++)
+ if (match(a[i], /^(((https?|ftp|file|news|irc):\/\/)|(mailto:)).+$/))
+ sub(a[i], dolink(a[i], a[i]))
+ # Inline.
+ subinline("(\\*\\*)|(__)", "strong")
+ subinline("\\*", "em")
+ subinline("`", "code")
+ text = text (text ? " " : "") $0
+ }
+
+ END {
+ while (ni > 0)
+ text = text "</" inline[ni--] ">"
+ newblock()
+ while (nl > 0)
+ print "</" list[nl--] ">"
+ }'
+}
+
+#serve() { while true; do busybox nc -kl -p 1500 -e "$cmd http_request"; done; }
+serve() { WAG_FUN=http_request busybox nc -kl -p 1500 -e "$cmd"; }
+
+test() {
+ fail=0 pass=0 skip=0 tfilter="$*"
+
+ test_run md2h 'abc __def__ ghi' '<p>abc <strong>def</strong> ghi</p>'
+ test_run md2h 'abc **def** ghi' '<p>abc <strong>def</strong> ghi</p>'
+ test_run md2h 'abc *def* ghi' '<p>abc <em>def</em> ghi</p>'
+ test_run md2h 'abc ***def*** ghi' '<p>abc <strong><em>def</strong></em> ghi</p>'
+ test_run md2h 'abc `def` ghi' '<p>abc <code>def</code> ghi</p>'
+ test_run md2h '# h1' '<h1>h1</h1>'
+ test_run md2h '## h2' '<h2>h2</h2>'
+ test_run md2h 'h1
+==' '<h1>h1</h1>'
+ test_run md2h 'h2
+--' '<h2>h2</h2>'
+ test_run md2h 'abc [github] def' '<p>abc <a href="">github</a> def</p>'
+ test_run md2h 'abc [github](https://github.com) def' '<p>abc <a href="https://github.com">github</a> def</p>'
+ test_run md2h 'abc [github] def
+
+[github]: https://github.com' '<p>abc <a href="https://github.com">github</a> def</p>'
+ test_run md2h '---
+Title: front matter test
+---
+
+Hello [world].
+
+---
+Bye.
+
+[world]: http://example.com' '<p>Hello <a href="http://example.com">world</a>.</p>
+<hr>
+<p>Bye.</p>'
+
+ echo "Total: $((pass + fail + skip)), Passed: $pass, Failed: $fail, Skip: $skip"
+ return "$fail"
+}
+
+test_run() {
+ eval "test_$1=\$((test_$1 + 1)); ti=\"\$test_$1\""
+ [ "$tfilter" ] &&
+ case "$1#$ti" in
+ $tfilter) ;;
+ *) skip=$((skip + 1)); return 0 ;;
+ esac
+ "$@"
+ [ "$got" = "$want" ] && {
+ pass=$((pass + 1))
+ return 0
+ }
+ fail=$((fail + 1))
+ printf "%s FAIL\n Got: %s\n Want: %s\n" "$1#$ti" "$got" "$want" >&2
+ return 1
+}
+
+[ "$WAG_FUN" ] && { $WAG_FUN; exit; }
+
+# Execute command line
+[ "$1" ] || help && "$@"
diff --git a/bin/wol b/bin/wol
new file mode 100755
index 0000000..7975df9
--- /dev/null
+++ b/bin/wol
@@ -0,0 +1,6 @@
+#!/bin/sh
+ip_plex=192.168.1.90 mac_plex=b0:83:fe:61:a7:aa
+eval "mac=\$mac_$1 ip=\$ip_$1"
+wakeonlan "$mac"
+while ! nc -zw 2 "$ip" 22; do printf .; sleep 2; done
+ssh "root@$ip"
diff --git a/bin/xt b/bin/xt
index 71b92d3..98f6ac6 100755
--- a/bin/xt
+++ b/bin/xt
@@ -1,4 +1,6 @@
#!/bin/sh
-exec xterm -sl 500 -j -cr red "$@" &
+#exec xterm -sl 500 -j -cr red "$@" &
+exec xterm "$@" &
#cmd="urxvtc ${@:--T $HOSTNAME}"
+#cmd="urxvtc $@"
#eval "$cmd" || { [ $? = 2 ] && urxvtd -q -o -f && eval "$cmd"; }
diff --git a/bin/yoda b/bin/yoda
index 8ff9e81..a5d5c5b 100755
--- a/bin/yoda
+++ b/bin/yoda
@@ -138,12 +138,13 @@ EOT
cat > /mnt/etc/locale.gen << \EOT
en_US.UTF-8 UTF-8
+en_GB.UTF-8 UTF-8
fr_FR.UTF-8 UTF-8
EOT
echo yoda > /mnt/etc/hostname
echo 'KEYMAP=fr-latin1' > /mnt/etc/vconsole.conf
- echo 'LANG=en_US.UTF-8' > /mnt/etc/locale.conf
+ echo 'LANG=en_GB.UTF-8' > /mnt/etc/locale.conf
ln -sf /usr/share/zoneinfo/Europe/Paris /mnt/etc/localtime
cat > /mnt/etc/systemd/network/20-wired.network << \EOT
@@ -171,6 +172,7 @@ EOT
cat > /mnt/etc/systemd/resolved.conf << \EOT
[Resolve]
DNSSEC=no
+DNSOverTLS=no
EOT
packages="linux linux-firmware intel-ucode amd-ucode sof-firmware \