If it becomes necessary, make skip-files file values default to
basenames but permit full paths to resolve ambiguities.

(1.9) Add updated version_parse subroutine from install.pl-1.8.

Fix bugs:

* ip-address matched inside longer addresses. With old 1.2.3.4 and new
  5.6.7.8, 1.8a turned 11.2.3.45 into 15.6.7.85 and 1.2.3.40 into
  5.6.7.80, then signed and shipped it. Matches now need clean
  boundaries.

* ip-address could substitute the word “null”. With no address found
  and return pressed, /dev/null in pf.conf became /dev/5.6.7.8. Return
  now only accepts a real address. The DNS lookup is now IPv4-only, so
  an AAAA record can’t become the default. Octets with leading zeros
  like 010 are rejected.

* Substituted files lost their mode. hostname.igc0 went from 0640 to
  0644, and only pf.conf was patched back by hand. Both substitution
  routines now keep the source mode, and the pf.conf go-r stays as a
  backstop.

* Syslock groups leaked between files. With foo.conf (etcrare) and
  tool (local), both .grp files got etcrare local. Each package now
  gets only its own groups, and the plain bundle only plain/custom
  groups.

* Flavored packages died when two versions existed (“Cannot parse
  version 2.0-no_x11”). Versions are now compared without the
  flavor. It dies only if the newest version exists in two flavors,
  since that’s genuinely ambiguous.

* The December next-year-key warning never fired. The regex tested
  digits 3–4 of the year, not the month. Confirmed with a faked
  December clock.

* An aborted run left files behind:

= Package copies and .grp files stayed in /var/install/<host>. For the
 local host, install.pl would then install them. 1.9 removes files it
 created if the run dies before shipping, but never files left by an
 earlier run.
  
- The temp dir of staged configs stayed in /tmp after most dies,
because rmtree won’t remove a tree containing the current directory.

* doas.conf:

- gendoas.pl failures were ignored; the exit status and output are now
  checked.

- For the local host, gendoas.pl wrote the live /etc/doas.conf
  directly. 1.9 uses -o to write into the staging dir like other
  hosts.

- It now writes with -o into the staging dir for all hosts.

* ipv6-prefix:

- An invalid new prefix length now re-prompts instead of being
  accepted.

- Narrowing a /56 to a /60 died on any address sharing the first three
  hextets but outside the old delegation. Those addresses are now left
  alone.

- 1-character hextets are now accepted.

- Matching is case-insensitive, and the prefix is compared in
  canonical form.

* Smaller fixes:

- Packages are written and renamed with checks, so a full /tmp can’t
produce a truncated archive that gets signed.  INT/TERM/HUP now run
cleanup, and terminal echo is restored.

- EOF at any prompt dies; 1.8a looped forever on the new-IP prompt,
  which printed 1.5 million times in the test.

- -k rejects paths, as in install.pl.

- Config checks:

  a. Host names in host-list are validated, since they become paths and rsync arguments.
  b. dest: must be absolute.
  c. Multi-file file: and dest: counts must match. This is checked when the config is read, before any prompts.
  d.A single-file custom entry with $HOST no longer fails the existence check.

* gendoas.pl 1.6:

- -o is always respected. /etc/doas.conf is only written when there’s
   no -o and the target is this host. In 1.5, -o for its own host went
   for /etc anyway.

- More than one host argument is rejected. 1.5 ignored the extras and
  rewrote this host’s live doas.conf.

- Each generated file is checked with doas -C before it’s written. A
  broken doas.conf can lock you out of root. If doas isn’t present,
  gendoas.pl says the check was skipped. The pledge now adds proc,
  exec and fattr, and unveils doas.

- File modes are set explicitly instead of coming from the umask. The
  live file keeps its existing mode, and so does the backup. Files
  written with -o or for other hosts, and a first live install, are
  0600. In 1.5 these came out 0644. doas only cares that the file
  isn’t group- or world-writable, so 0600 works.

- A first install with no existing /etc/doas.conf now works. 1.5 died
  trying to back it up.

- The copy for other hosts and -o is checked.

- The header timestamp is local time. 1.5 took the time after unveil,
  which hid the timezone files, so it came out in UTC.

- A template that ends mid-continuation is rejected.

(1.8) Added only-files complement to skip-files. If a file is
referenced in only-files, only the hosts listed get it; file
cannot be listed in both only-files and skip-files.

(1.7a) Add ipv6-prefix rewrite case for longer prefixes within
the delegation.

(1.7) Add _custom_ipv6_prefix and ipv6-prefix custom type.
Supports same-size changes for /56 and /60, and will do
its best to do a /56 to /60 change with orphaned address
detection.

(1.6a) Add optional custom-var ipv6-type to _custom_ip_address (default
is "tunnel", used only in output messages.  Refactor _custom_ip_address
into _custom_substitute_files for the main engine and _custom_ip_address
for the input collection and validation, in preparation to add another
custom type for IPv6 prefix delegation changes.

(1.6) Add custom-vars require-source-dir and skip-files to
_custom_ip_address (ip-address). This allows for different
hosts to receive different config files and to not run if
a required source dir is missing (e.g., pulling file configs
to change from a backup that is offline). Change rsync path
for Linux or macOS (though this is still untested off-OpenBSD).

(1.5) Allow distribute.pl to distribute to the host it's run on.

(1.4) Fix bugs identified by Claude Opus 4.8 review
    fixed new-IP-address range validation, %s->%S timestamp, add_files
    partial-failure check, passphrase/temp cleanup via END block, OpenBSD
    version-parse fallback, and regex metachar quoting
    (host/$INSTALL_DIR, pkg_start).

(1.3) Add syslock grp files for packages when required.

(1.2a): Make second signature verification also fail fast.

(1.2): Collect error codes from rsync calls and report failed hosts
at the end.  (gendoas 1.4: wrap pledge/unveil with conditional to only execute
on OpenBSD).

distribute: Changed name of $vv to $v_epoch (DONE)

Add a -h option to distribute to specify that a subset of the hosts
will get the files. -h would take either a single host name or a
comma-separated list; if any hosts in the list aren't identified
in the config as a recipient for a particular file specified, those
hosts don't get it (and perhaps print a warning to that effect, maybe
with a -v option). (DONE)

Add a -d debug option to print more processing information. (DONE)

Split into part that either runs as root or uses doas to get the files
to copy (though the vast majority are world readable), and runs non-priv
to do the building, signing, and distribution.
