commit bbae32aa29d8d4c86c2c5084d65a0671c037cb04
parent 35deefa23a8925b483fae697f749a2f6cd4a0e32
Author: Jacob R. Edwards <n/a>
Date: Fri, 11 Feb 2022 16:42:01 -0800
Update module installing system
The new program (a shell script) doesn't support linking files.
Instead, it shows a diff and allows the user to decide to update
the target file or not.
That it shows a diff is a major improvement, the old program didn't
tell you anything except that there was a conflict, and you had to
manually resolve them. Perhaps linking could be re-incorperated,
keeping the diffs and conflict resolution.
Diffstat:
9 files changed, 133 insertions(+), 215 deletions(-)
diff --git a/Makefile b/Makefile
@@ -1,5 +1,5 @@
-subdirs = local
-
-all: install
+subdirs =\
+ global\
+ local\
.include "lib/subdirs.make"
diff --git a/global/Makefile b/global/Makefile
@@ -1,5 +1,5 @@
lib = ../lib
-act = force copy
+flags = -o root:wheel
dest = /
mods =\
doas\
diff --git a/lib/Makefile b/lib/Makefile
@@ -1,22 +0,0 @@
-cc = cc
-cflags = -Wall -Wno-write-strings
-ldflags = -static
-src = module.c
-name = module
-
-all: ${name}
-
-.c.o:
- ${cc} ${cflags} -c -o $@ $<
-
-${name}: Makefile
-
-${name}: ${name}.c
- ${cc} ${cflags} -o ${name} ${name}.c ${ldflags}
-
-
-clean:
- rm -f ${name}
-
-.SUFFIXES: .c .o
-.PHONY: clean install uninstall
diff --git a/lib/commands.make b/lib/commands.make
@@ -0,0 +1,3 @@
+# Sync script commands (first is default)
+
+commands = push pull diff stop
diff --git a/lib/modsync b/lib/modsync
@@ -0,0 +1,117 @@
+#!/bin/sh
+# Copyright 2022 Jacob R. Edwards
+# Configuration module syncing script
+#
+# This script should conform to POSIX, because why not:
+POSIXLY_CORRECT=
+
+error() {
+ echo "$*" 1>&2
+ exit 1
+}
+
+usererror() {
+ error "usage: $name [push|pull|diff|stop] [-dnop] dest module ..."
+ test "${1:-}" &&
+ error "$@"
+}
+
+ask() {
+ # No yes-forever answer, use yes(1)
+ test -t &&
+ echo -n "$1? [$2] " 1>&2
+ sed '1{ s/^$/'$2'/; q; }' | grep -q '^[Yy]'
+}
+
+# Could be dynamically generated for efficiency, install(1) is also
+# an option.
+install() {
+ echo "$2" 1>&2
+ mkdir -p "$(dirname "$2")"
+ cp -p "$1" "$2"
+ test "$owner" &&
+ chown "$owner" "$2"
+ test "$perms" &&
+ chmod "$perms" "$2"
+}
+
+filesync() {
+ # Fix diff(1) output, I cannot get it to produce it for the
+ # file I want when pulling.
+ if ! test -f "$2"
+ then
+ install "$1" "$2"
+ elif $forcediff || test "$1" -nt "$2" -o "$1" -ot "$2"
+ then
+ if ! diff -u "$2" "$1" 1>&2
+ then
+ $patch && ask patch y &&
+ install "$1" "$2"
+ else
+ touch -r "$1" "$2"
+ fi
+ fi
+}
+
+modsync() {
+ cmd="$1"
+ mod="$2"
+ dir="$3"
+
+ case "$cmd" in
+ (push) sync() filesync "$1" "$2" ;;
+ (pull) sync() filesync "$2" "$1" ;;
+ (stop) sync() rm -v "$2" ;;
+ (*) usererror "$name: '$1': Invalid command" ;;
+ esac
+
+ for file in $(cd "$mod" && find . -type f | cut -c 3-)
+ do
+ sync "$mod/$file" "$dir/$file"
+ done
+}
+
+# Unset variables are unchecked on purpose, nounset (-u) handles it
+set -eu
+
+IFS='
+'
+name="$(basename "$0")"
+
+# Assume exit is due to user error, namely an unset variable
+trap usererror 0
+
+if test "$1" = 'diff'
+then
+ shift
+ set -- push -n "$@"
+fi
+
+cmd="$1"
+shift
+
+patch=true
+owner=
+perms=
+forcediff=false
+while expr x"$1" : x- > /dev/null
+do
+ case "$1" in
+ (-n) patch=false ;;
+ (-o) owner="$2"; shift ;;
+ (-p) perms="$2"; shift ;;
+ (-d) forcediff=true ;;
+ (*) usererror "$name: '$1': Invalid option" ;;
+ esac
+ shift
+done
+
+dir="$1"
+shift
+
+trap "" 0
+
+for mod in "$@"
+do
+ modsync "$cmd" "$mod" "$dir"
+done
diff --git a/lib/module.c b/lib/module.c
@@ -1,176 +0,0 @@
-#define const
-
-#include <sys/stat.h>
-#include <sys/wait.h>
-
-#include <errno.h>
-#include <fts.h>
-#include <limits.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#define SHIFT(N) { argc -= N; argv += N; }
-
-static int argtoo; /* Install given directories too, not just their contents. */
-static int force; /* Install even if the target exists. */
-static int (*action)(char *, char *); /* Install function (link, copy, or none). */
-static int check; /* Ask for confirmation instead of exiting. */
-
-void
-die(char *s)
-{
- perror(s);
- exit(1);
-}
-
-int
-copy(char *a, char *b)
-{
- int status;
- pid_t pid;
-
- pid = fork();
- if (pid == 0) {
- execv("/bin/cp", (char *[]){ "cp", "-p", a, b, NULL });
- perror("/bin/cp");
- _exit(1);
- }
-
- if (pid < 0 || waitpid(pid, &status, 0) < 0)
- return 1;
- return WEXITSTATUS(status);
-}
-
-int
-none(char *a, char *b)
-{
- return 0;
-}
-
-int
-confirm(char *a, char *b)
-{
- char c, answer;
-
- if (force)
- return 1;
- if (!check)
- return 0;
-
- fprintf(stderr, "%s -> %s? ", a, b);
- answer = fgetc(stdin);
- do
- c = fgetc(stdin);
- while (c != '\n' && c != EOF);
- return answer == 'y' || answer == 'Y';
-}
-
-int
-main(int argc, char *argv[])
-{
- FTS *fts;
- FTSENT *ent;
- char new[PATH_MAX];
- char prefix[PATH_MAX];
- int status;
- size_t arglen;
- struct stat st;
- char *progname;
-
- progname = *argv;
- if (argc < 4) {
-usage:
- fprintf(stderr, "usage: %s [force | check] action source ... target\n", progname);
- return 1;
- }
-
- SHIFT(1);
- if (strcmp(*argv, "force") == 0) {
- force = 1;
- SHIFT(1);
- } else if (strcmp(*argv, "check") == 0) {
- check = 1;
- SHIFT(1);
- }
-
- if (strcmp(argv[0], "copy") == 0)
- action = copy;
- else if (strcmp(argv[0], "link") == 0)
- action = link;
- else {
- action = none;
- if (strcmp(argv[0], "remove") == 0)
- force = 1;
- else if (strcmp(argv[0], "list") == 0)
- force = 0;
- else
- goto usage;
- }
- SHIFT(1);
-
- if (realpath(argv[--argc], prefix) == NULL)
- die(argv[argc]);
- argv[argc] = NULL;
-
- fts = fts_open(argv, 0, NULL);
- if (fts == NULL)
- die("fts_open");
-
- arglen = 0;
- while ((ent = fts_read(fts))) {
- if (!argtoo && ent->fts_level == 0 && ent->fts_info == FTS_D) {
- arglen = strlen(ent->fts_path);
- if (ent->fts_path[arglen - 1] != '/')
- ++arglen;
- continue;
- }
- if (snprintf(new, sizeof(new), "%s/%s", prefix, ent->fts_path + arglen) >= sizeof(new)) {
- errno = ENAMETOOLONG;
- die(ent->fts_path);
- }
-
- errno = 0;
- if (stat(new, &st) < 0 && errno != ENOENT)
- die(new);
-
- status = 0;
- switch (ent->fts_info) {
- case FTS_DNR:
- case FTS_ERR:
- case FTS_NS:
- errno = ent->fts_errno;
- die(ent->fts_path);
- case FTS_DP:
- break;
- case FTS_D:
- if (errno == ENOENT || !S_ISDIR(st.st_mode))
- status = mkdir(new, ent->fts_statp->st_mode);
- break;
- case FTS_F:
- if (errno != ENOENT && action == link && st.st_ino == ent->fts_statp->st_ino)
- ;
- else {
- if (errno == ENOENT)
- status = action(ent->fts_accpath, new);
- else if (confirm(ent->fts_accpath, new))
- status = (unlink(new) || action(ent->fts_accpath, new));
- else if (!check && action != none) {
- errno = EEXIST;
- status = 1;
- }
- if (!status)
- puts(new);
- }
- break;
- default:
- fprintf(stderr, "%s: Not a regular file.\n", ent->fts_path);
- status = 1;
- }
- if (status)
- die(new);
- }
-
- return fts_close(fts) != 0;
-}
diff --git a/lib/module.make b/lib/module.make
@@ -1,15 +1,8 @@
# Module targets
-all: install
+.include "commands.make"
-lib:
- make -C ${lib}
-
-install: lib
- ${lib}/module ${act} ${mods} ${dest}
-
-uninstall: lib
- ${lib}/module remove ${mods} ${dest}
-
-.PHONY: all lib install uninstall
+${commands}:
+ ${lib}/modsync $@ ${flags} ${userflags} ${dest} ${mods}
+.PHONY: ${commands}
diff --git a/lib/subdirs.make b/lib/subdirs.make
@@ -1,6 +1,10 @@
# Make sub-directories
-install uninstall:
+.include "commands.make"
+
+${commands}:
.for subdir in ${subdirs}
make -C ${subdir} $@
.endfor
+
+.PHONY: ${commands}
diff --git a/local/Makefile b/local/Makefile
@@ -1,5 +1,4 @@
lib = ../lib
-act = link
dest = ${HOME}
mods =\
bin\