config

OpenBSD system configuration
git clone git://jacobedwards.org/config
Log | Files | Refs | README

modsync (2958B)


      1 #!/bin/sh
      2 # Copyright 2022 Jacob R. Edwards
      3 # Configuration module syncing script
      4 #
      5 # This script should conform to POSIX, because why not:
      6 POSIXLY_CORRECT=
      7 
      8 error() {
      9 	echo "$*" 1>&2
     10 	exit 1
     11 }
     12 
     13 usererror() {
     14 	error "usage: $name [push|pull|diff|stop] [-cdnop] [-s subs] dest module ..."
     15 	test "${1:-}" &&
     16 		error "$@"
     17 }
     18 
     19 ask() {
     20 	test -t 0 -a -t 2 &&
     21 		echo -n "$1? [$2] " 1>&2
     22 	awk -vd="$2" '{ a=$0; exit } END{ if(!a) a=d; exit a !~ "^[Yy]" }'
     23 }
     24 
     25 # Could be dynamically generated for efficiency, install(1) is also
     26 # an option.
     27 update() {
     28 	cp -p "$1" "$2"
     29 	test "$owner" &&
     30 		chown "$owner" "$2"
     31 	test "$perms" &&
     32 		chmod "$perms" "$2"
     33 }
     34 
     35 install() {
     36 	mkdir -p "$(dirname "$2")"
     37         # If there is an error, it's likely that we're on a different
     38         # filesystem. If not, cp(1) will likely have the same one.
     39 	! $fcopy &&
     40 		ln "$1" "$2" 2>/dev/null && return
     41 	update "$1" "$2"
     42 }
     43 
     44 # TODO: Differing source mode, owner, etc. should be updated in dest 
     45 filesync() {
     46         # Fix diff(1) output, I cannot get it to produce it for the
     47         # file I want when pulling.
     48 
     49 	if test "$1" -ef "$2"
     50 	then
     51 		return
     52 	elif ! test -f "$2"
     53 	then
     54 		echo "$2" 1>&2
     55 		install "$1" "$2"
     56 	elif $fdiff || test "$1" -nt "$2" -o "$1" -ot "$2"
     57 	then
     58 		if ! diff -u "$2" "$1" 1>&2
     59 		then
     60 			$patch && ask "patch $2" y &&
     61 				update "$1" "$2"
     62 		else
     63 			touch -r "$1" "$2"
     64 		fi
     65 	fi
     66 	return 0
     67 }
     68 
     69 addsub() {
     70 	awk -vOFS="$IFS2" -F/ -vsubs="$1" 'BEGIN {
     71 		len = split(subs, a, ",");
     72 		for (i = 1; i <= len; ++i) {
     73 			if (!(n = index(a[i], "="))) {
     74 				print "Invalid substitution" > "/dev/stderr";
     75 				exit 1;
     76 			}
     77 			t[substr(a[i], 1, n - 1)] = substr(a[i], n + 1);
     78 		}
     79 	}
     80 
     81 	{
     82 		new = $0
     83 		if ($1 in t)
     84 			new = t[$1] substr(new, length($1) + 1);
     85 		print $0, new
     86 	}'
     87 }
     88 
     89 modsync() {
     90 	cmd="$1"
     91 	mod="$2"
     92 	dir="$3"
     93 
     94 	case "$cmd" in
     95 	(push) sync() filesync "$1" "$2" ;;
     96 	(pull) sync() filesync "$2" "$1" ;;
     97 	(stop) sync() rm -v "$2" ;;
     98 	(*) usererror "$name: '$1': Invalid command" ;;
     99 	esac
    100 
    101 	for files in $(cd "$mod" && find . -type f | cut -c 3- | addsub "$subs")
    102 	do (
    103 		IFS="$IFS2"
    104 		set -- $files
    105 		test $# -ne 2 && {
    106 			echo 'error: fs (034) character in path' 1>&2
    107 			exit 1
    108 		}
    109 		sync "$mod/$1" "$dir/$2"
    110 	) done
    111 }
    112 
    113 # Unset variables are unchecked on purpose, nounset (-u) handles it
    114 set -eu
    115 
    116 IFS='
    117 '
    118 # \034 (fs)
    119 IFS2=''
    120 name="$(basename "$0")"
    121 
    122 # Assume exit is due to user error, namely an unset variable
    123 trap usererror 0
    124 
    125 if test "$1" = 'diff'
    126 then
    127 	shift
    128 	set -- push -n "$@"
    129 fi
    130 
    131 cmd="$1"
    132 shift
    133 
    134 patch=true
    135 owner=
    136 perms=
    137 fdiff=false
    138 fcopy=false
    139 subs=
    140 while expr x"$1" : x- > /dev/null
    141 do
    142 	case "$1" in
    143 	(-n) patch=false ;;
    144 	(-o) owner="$2"; shift ;;
    145 	(-p) perms="$2"; shift ;;
    146 	(-d) fdiff=true ;;
    147 	(-c) fcopy=true ;;
    148 	(-s) subs="$2"; shift ;;
    149 	(*) usererror "$name: '$1': Invalid option" ;;
    150 	esac
    151 	shift
    152 done
    153 
    154 dir="${1:%/}"
    155 shift
    156 
    157 trap "" 0
    158 
    159 if test "$cmd" = stop && $patch
    160 then
    161 	"$0" pull "$dir" "$@"
    162 fi
    163 
    164 for mod in "$@"
    165 do
    166 	modsync "$cmd" "$mod" "$dir"
    167 done
    168 exit 0