about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2023-02-11 03:12:21 +0000
committerLaurent Bercot <ska@appnovation.com>2023-02-11 03:12:21 +0000
commit774654ad56fc9304e4a064232760835c7a2a6c13 (patch)
tree3f539521073866497ff0370d8a6464c9356cadb4
parent790c8681d3451b61a536871dad234fc294796fd8 (diff)
downloads6-portable-utils-774654ad56fc9304e4a064232760835c7a2a6c13.tar.gz
s6-portable-utils-774654ad56fc9304e4a064232760835c7a2a6c13.tar.xz
s6-portable-utils-774654ad56fc9304e4a064232760835c7a2a6c13.zip
Add multicall configuration
Signed-off-by: Laurent Bercot <ska@appnovation.com>
-rw-r--r--.gitignore1
-rw-r--r--Makefile30
-rwxr-xr-xconfigure15
-rw-r--r--doc/index.html11
-rw-r--r--doc/s6-portable-utils.html53
-rw-r--r--package/deps.mak180
-rw-r--r--package/modes1
-rw-r--r--package/targets.mak68
-rw-r--r--src/multicall/deps-exe/s6-portable-utils2
-rw-r--r--src/s6-portable-utils/deps-exe/s6-basename (renamed from src/skaembutils/deps-exe/s6-basename)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-cat (renamed from src/skaembutils/deps-exe/s6-cat)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-chmod (renamed from src/skaembutils/deps-exe/s6-chmod)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-chown (renamed from src/skaembutils/deps-exe/s6-chown)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-clock (renamed from src/skaembutils/deps-exe/s6-clock)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-cut (renamed from src/skaembutils/deps-exe/s6-cut)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-dirname (renamed from src/skaembutils/deps-exe/s6-dirname)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-dumpenv (renamed from src/skaembutils/deps-exe/s6-dumpenv)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-echo (renamed from src/skaembutils/deps-exe/s6-echo)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-env (renamed from src/skaembutils/deps-exe/s6-env)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-expr (renamed from src/skaembutils/deps-exe/s6-expr)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-false (renamed from src/skaembutils/deps-exe/s6-false)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-format-filter (renamed from src/skaembutils/deps-exe/s6-format-filter)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-grep (renamed from src/skaembutils/deps-exe/s6-grep)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-head (renamed from src/skaembutils/deps-exe/s6-head)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-hiercopy (renamed from src/skaembutils/deps-exe/s6-hiercopy)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-linkname (renamed from src/skaembutils/deps-exe/s6-linkname)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-ln (renamed from src/skaembutils/deps-exe/s6-ls)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-ls (renamed from src/skaembutils/deps-exe/s6-mkdir)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-maximumtime (renamed from src/skaembutils/deps-exe/s6-maximumtime)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-mkdir (renamed from src/skaembutils/deps-exe/s6-mkfifo)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-mkfifo (renamed from src/skaembutils/deps-exe/s6-nice)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-nice (renamed from src/skaembutils/deps-exe/s6-nuke)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-nuke (renamed from src/skaembutils/deps-exe/s6-pause)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-pause (renamed from src/skaembutils/deps-exe/s6-printenv)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-printenv (renamed from src/skaembutils/deps-exe/s6-quote)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-quote (renamed from src/skaembutils/deps-exe/s6-quote-filter)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-quote-filter (renamed from src/skaembutils/deps-exe/s6-rename)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-rename (renamed from src/skaembutils/deps-exe/s6-rmrf)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-rmrf (renamed from src/skaembutils/deps-exe/s6-seq)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-seq (renamed from src/skaembutils/deps-exe/s6-sort)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-sleep (renamed from src/skaembutils/deps-exe/s6-sleep)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-sort (renamed from src/skaembutils/deps-exe/s6-tai64ndiff)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-sync (renamed from src/skaembutils/deps-exe/s6-sync)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-tai64ndiff (renamed from src/skaembutils/deps-exe/s6-tail)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-tail (renamed from src/skaembutils/deps-exe/s6-touch)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-touch (renamed from src/skaembutils/deps-exe/s6-unquote)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-true (renamed from src/skaembutils/deps-exe/s6-true)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-uniquename (renamed from src/skaembutils/deps-exe/s6-unquote-filter)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-unquote (renamed from src/skaembutils/deps-exe/seekablepipe)0
-rw-r--r--src/s6-portable-utils/deps-exe/s6-unquote-filter1
-rw-r--r--src/s6-portable-utils/deps-exe/s6-update-symlinks1
-rw-r--r--src/s6-portable-utils/deps-exe/seekablepipe1
-rw-r--r--src/s6-portable-utils/s6-basename.c (renamed from src/skaembutils/s6-basename.c)0
-rw-r--r--src/s6-portable-utils/s6-cat.c (renamed from src/skaembutils/s6-cat.c)0
-rw-r--r--src/s6-portable-utils/s6-chmod.c (renamed from src/skaembutils/s6-chmod.c)0
-rw-r--r--src/s6-portable-utils/s6-chown.c (renamed from src/skaembutils/s6-chown.c)0
-rw-r--r--src/s6-portable-utils/s6-clock.c (renamed from src/skaembutils/s6-clock.c)0
-rw-r--r--src/s6-portable-utils/s6-cut.c (renamed from src/skaembutils/s6-cut.c)0
-rw-r--r--src/s6-portable-utils/s6-dirname.c (renamed from src/skaembutils/s6-dirname.c)0
-rw-r--r--src/s6-portable-utils/s6-dumpenv.c (renamed from src/skaembutils/s6-dumpenv.c)0
-rw-r--r--src/s6-portable-utils/s6-echo.c (renamed from src/skaembutils/s6-echo.c)0
-rw-r--r--src/s6-portable-utils/s6-env.c (renamed from src/skaembutils/s6-env.c)0
-rw-r--r--src/s6-portable-utils/s6-expr.c (renamed from src/skaembutils/s6-expr.c)0
-rw-r--r--src/s6-portable-utils/s6-false.c (renamed from src/skaembutils/s6-false.c)0
-rw-r--r--src/s6-portable-utils/s6-format-filter.c (renamed from src/skaembutils/s6-format-filter.c)0
-rw-r--r--src/s6-portable-utils/s6-grep.c (renamed from src/skaembutils/s6-grep.c)0
-rw-r--r--src/s6-portable-utils/s6-head.c (renamed from src/skaembutils/s6-head.c)0
-rw-r--r--src/s6-portable-utils/s6-hiercopy.c (renamed from src/skaembutils/s6-hiercopy.c)0
-rw-r--r--src/s6-portable-utils/s6-linkname.c (renamed from src/skaembutils/s6-linkname.c)0
-rw-r--r--src/s6-portable-utils/s6-ln.c (renamed from src/skaembutils/s6-ln.c)6
-rw-r--r--src/s6-portable-utils/s6-ls.c (renamed from src/skaembutils/s6-ls.c)0
-rw-r--r--src/s6-portable-utils/s6-maximumtime.c (renamed from src/skaembutils/s6-maximumtime.c)0
-rw-r--r--src/s6-portable-utils/s6-mkdir.c (renamed from src/skaembutils/s6-mkdir.c)0
-rw-r--r--src/s6-portable-utils/s6-mkfifo.c (renamed from src/skaembutils/s6-mkfifo.c)0
-rw-r--r--src/s6-portable-utils/s6-nice.c (renamed from src/skaembutils/s6-nice.c)0
-rw-r--r--src/s6-portable-utils/s6-nuke.c (renamed from src/skaembutils/s6-nuke.c)0
-rw-r--r--src/s6-portable-utils/s6-pause.c (renamed from src/skaembutils/s6-pause.c)0
-rw-r--r--src/s6-portable-utils/s6-printenv.c (renamed from src/skaembutils/s6-printenv.c)0
-rw-r--r--src/s6-portable-utils/s6-quote-filter.c (renamed from src/skaembutils/s6-quote-filter.c)0
-rw-r--r--src/s6-portable-utils/s6-quote.c (renamed from src/skaembutils/s6-quote.c)0
-rw-r--r--src/s6-portable-utils/s6-rename.c (renamed from src/skaembutils/s6-rename.c)0
-rw-r--r--src/s6-portable-utils/s6-rmrf.c (renamed from src/skaembutils/s6-rmrf.c)0
-rw-r--r--src/s6-portable-utils/s6-seq.c (renamed from src/skaembutils/s6-seq.c)0
-rw-r--r--src/s6-portable-utils/s6-sleep.c (renamed from src/skaembutils/s6-sleep.c)0
-rw-r--r--src/s6-portable-utils/s6-sort.c (renamed from src/skaembutils/s6-sort.c)0
-rw-r--r--src/s6-portable-utils/s6-sync.c (renamed from src/skaembutils/s6-sync.c)0
-rw-r--r--src/s6-portable-utils/s6-tai64ndiff.c (renamed from src/skaembutils/s6-tai64ndiff.c)0
-rw-r--r--src/s6-portable-utils/s6-tail.c (renamed from src/skaembutils/s6-tail.c)0
-rw-r--r--src/s6-portable-utils/s6-touch.c (renamed from src/skaembutils/s6-touch.c)0
-rw-r--r--src/s6-portable-utils/s6-true.c (renamed from src/skaembutils/s6-true.c)0
-rw-r--r--src/s6-portable-utils/s6-uniquename.c (renamed from src/skaembutils/s6-uniquename.c)0
-rw-r--r--src/s6-portable-utils/s6-unquote-filter.c (renamed from src/skaembutils/s6-unquote-filter.c)0
-rw-r--r--src/s6-portable-utils/s6-unquote.c (renamed from src/skaembutils/s6-unquote.c)0
-rw-r--r--src/s6-portable-utils/s6-update-symlinks.c (renamed from src/skaembutils/s6-update-symlinks.c)75
-rw-r--r--src/s6-portable-utils/seekablepipe.c (renamed from src/skaembutils/seekablepipe.c)0
-rw-r--r--src/skaembutils/deps-exe/s6-ln3
-rw-r--r--src/skaembutils/deps-exe/s6-uniquename3
-rw-r--r--src/skaembutils/deps-exe/s6-update-symlinks3
-rwxr-xr-xtools/gen-multicall.sh84
-rwxr-xr-xtools/install.sh11
100 files changed, 348 insertions, 201 deletions
diff --git a/.gitignore b/.gitignore
index 6e2876c..d77920a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,3 +49,4 @@
 /s6-unquote-filter
 /s6-update-symlinks
 /seekablepipe
+/s6-portable-utils
diff --git a/Makefile b/Makefile
index cd6c7f0..0fc3d40 100644
--- a/Makefile
+++ b/Makefile
@@ -17,8 +17,9 @@ CC = $(error Please use ./configure first)
 STATIC_LIBS :=
 SHARED_LIBS :=
 INTERNAL_LIBS :=
-EXTRA_TARGETS :=
 LIB_DEFS :=
+BIN_SYMLINKS :=
+EXTRA_TEMP :=
 
 define library_definition
 LIB$(firstword $(subst =, ,$(1))) := lib$(lastword $(subst =, ,$(1))).$(if $(DO_ALLSTATIC),a,so).xyzzy
@@ -53,14 +54,14 @@ RANLIB := $(CROSS_COMPILE)ranlib
 STRIP := $(CROSS_COMPILE)strip
 INSTALL := ./tools/install.sh
 
-ALL_BINS := $(LIBEXEC_TARGETS) $(BIN_TARGETS)
+ALL_BINS := $(BIN_TARGETS) $(LIBEXEC_TARGETS)
 ALL_LIBS := $(SHARED_LIBS) $(STATIC_LIBS) $(INTERNAL_LIBS)
 ALL_INCLUDES := $(wildcard src/include/$(package)/*.h)
 
 all: $(ALL_LIBS) $(ALL_BINS) $(ALL_INCLUDES)
 
 clean:
-	@exec rm -f $(ALL_LIBS) $(ALL_BINS) $(wildcard src/*/*.o src/*/*.lo) $(EXTRA_TARGETS)
+	@exec rm -f $(ALL_LIBS) $(ALL_BINS) $(EXTRA_TEMP) $(wildcard src/*/*.o src/*/*.lo)
 
 distclean: clean
 	@exec rm -f config.mak src/include/$(package)/config.h
@@ -84,7 +85,7 @@ endif
 install: install-dynlib install-libexec install-bin install-lib install-include
 install-dynlib: $(SHARED_LIBS:lib%.so.xyzzy=$(DESTDIR)$(dynlibdir)/lib%.so)
 install-libexec: $(LIBEXEC_TARGETS:%=$(DESTDIR)$(libexecdir)/%)
-install-bin: $(BIN_TARGETS:%=$(DESTDIR)$(bindir)/%)
+install-bin: $(BIN_TARGETS:%=$(DESTDIR)$(bindir)/%) $(BIN_SYMLINKS:%=$(DESTDIR)$(bindir)/%)
 install-lib: $(STATIC_LIBS:lib%.a.xyzzy=$(DESTDIR)$(libdir)/lib%.a)
 install-include: $(ALL_INCLUDES:src/include/$(package)/%.h=$(DESTDIR)$(includedir)/$(package)/%.h)
 install-data: $(ALL_DATA:src/etc/%=$(DESTDIR)$(datadir)/%)
@@ -96,7 +97,7 @@ $(DESTDIR)$(exthome): $(DESTDIR)$(home)
 
 update: $(DESTDIR)$(exthome)
 
-global-links: $(DESTDIR)$(exthome) $(SHARED_LIBS:lib%.so.xyzzy=$(DESTDIR)$(sproot)/library.so/lib%.so.$(version_M)) $(BIN_TARGETS:%=$(DESTDIR)$(sproot)/command/%)
+global-links: $(DESTDIR)$(exthome) $(SHARED_LIBS:lib%.so.xyzzy=$(DESTDIR)$(sproot)/library.so/lib%.so.$(version_M)) $(BIN_TARGETS:%=$(DESTDIR)$(sproot)/command/%) $(BIN_SYMLINKS:%=$(DESTDIR)$(sproot)/command/%)
 
 $(DESTDIR)$(sproot)/command/%: $(DESTDIR)$(home)/command/%
 	exec $(INSTALL) -D -l ..$(subst $(sproot),,$(exthome))/command/$(<F) $@
@@ -116,11 +117,20 @@ $(DESTDIR)$(dynlibdir)/lib%.so $(DESTDIR)$(dynlibdir)/lib%.so.$(version_M): lib%
 	$(INSTALL) -l $(@F).$(version) $@.$(version_M) && \
 	exec $(INSTALL) -l $(@F).$(version_M) $@
 
-$(DESTDIR)$(libexecdir)/% $(DESTDIR)$(bindir)/%: % package/modes
-	exec $(INSTALL) -D -m 600 $< $@
-	grep -- ^$(@F) < package/modes | { read name mode owner && \
-	if [ x$$owner != x ] ; then chown -- $$owner $@ ; fi && \
-	chmod $$mode $@ ; }
+$(LIBEXEC_TARGETS:%=$(DESTDIR)$(libexecdir)/%): $(DESTDIR)$(libexecdir)/%: % package/modes
+$(BIN_TARGETS:%=$(DESTDIR)$(bindir)/%): $(DESTDIR)$(bindir)/%: % package/modes
+$(LIBEXEC_TARGETS:%=$(DESTDIR)$(libexecdir)/%) $(BIN_TARGETS:%=$(DESTDIR)$(bindir)/%):
+	grep -- ^$(@F) < package/modes | { read name mode og && \
+	if [ x$$og != x ] ; then og="-O $${og}" ; fi && \
+	$(INSTALL) -D -m $$mode $$og $< $@ ; }
+
+define install_symlink_rule
+$(DESTDIR)$(bindir)/$(1): $(DESTDIR)$(bindir)/$$(SYMLINK_TARGET_$(1))
+endef
+
+$(foreach x,$(BIN_SYMLINKS),$(eval $(call install_symlink_rule,$(x))))
+$(BIN_SYMLINKS:%=$(DESTDIR)$(bindir)/%):
+	exec $(INSTALL) -l $(SYMLINK_TARGET_$(@F)) $@
 
 $(DESTDIR)$(libdir)/lib%.a: lib%.a.xyzzy
 	exec $(INSTALL) -D -m 644 $< $@
diff --git a/configure b/configure
index 6846938..5835905 100755
--- a/configure
+++ b/configure
@@ -45,6 +45,7 @@ Optional features:
   --enable-slashpackage[=ROOT]  assume /package installation at ROOT [disabled]
   --enable-absolute-paths       do not rely on PATH to access this package's binaries,
                                   hardcode absolute BINDIR/foobar paths instead [disabled]
+  --enable-multicall            build a multicall binary [disabled]
 
 EOF
 exit 0
@@ -147,6 +148,7 @@ static=true
 allpic=true
 slashpackage=false
 abspath=false
+multicall=false
 sproot=
 home=
 exthome=
@@ -188,6 +190,8 @@ for arg ; do
     --disable-slashpackage) sproot= ; slashpackage=false ;;
     --enable-absolute-paths|--enable-absolute-paths=yes) abspath=true ;;
     --disable-absolute-paths|--enable-absolute-paths=no) abspath=false ;;
+    --enable-multicall|--enable-multicall=yes) multicall=true ;;
+    --disable-multicall|--enable-multicall=no) multicall=false ;;
     --enable-*|--disable-*|--with-*|--without-*|--*dir=*) ;;
     --host=*|--target=*) target=${arg#*=} ;;
     --build=*) build=${arg#*=} ;;
@@ -321,15 +325,15 @@ if [ "x$target" != "x$(cat $sysdeps/target)" ] ; then
   exit 1
 fi
 
+if $allpic ; then
+  tryflag CPPFLAGS_AUTO -fPIC
+fi
 spawn_lib=$(cat $sysdeps/spawn.lib)
 socket_lib=$(cat $sysdeps/socket.lib)
 sysclock_lib=$(cat $sysdeps/sysclock.lib)
 timer_lib=$(cat $sysdeps/timer.lib)
 util_lib=$(cat $sysdeps/util.lib)
 
-if $allpic ; then
-  tryflag CPPFLAGS_AUTO -fPIC
-fi
 tryflag CFLAGS_AUTO -std=c99
 tryflag CFLAGS -fomit-frame-pointer
 tryflag CFLAGS_AUTO -fno-exceptions
@@ -437,6 +441,11 @@ if $allpic ; then
 else
   echo "STATIC_LIBS_ARE_PIC :="
 fi
+if $multicall ; then
+  echo "MULTICALL := 1"
+else
+  echo "MULTICALL :="
+fi
 
 exec 1>&3 3>&-
 echo "  ... done."
diff --git a/doc/index.html b/doc/index.html
index 3d8c400..69f753f 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -104,6 +104,8 @@ of the s6-portable-utils git repository. </li>
 
 <ul>
  <li> See the enclosed INSTALL file for installation details. </li>
+ <li> Starting with version 2.3.0.0, you can use the
+<tt>--enable-multicall</tt> configure option to save disk space. </li>
 </ul>
 
 <h3> Upgrade notes </h3>
@@ -126,6 +128,9 @@ the previous versions of s6-portable-utils and the current one. </li>
 126 if they fail to execute into a program for another reason.
 </p>
 
+<p>
+ (Miscellaneous tools)
+</p>
 <ul>
  <li> The <a href="s6-basename.html"><tt>s6-basename</tt></a> program </li>
  <li> The <a href="s6-cat.html"><tt>s6-cat</tt></a> program </li>
@@ -171,6 +176,12 @@ the previous versions of s6-portable-utils and the current one. </li>
  <li> The <a href="s6-update-symlinks.html"><tt>s6-update-symlinks</tt></a> program </li>
  <li> The <a href="seekablepipe.html"><tt>seekablepipe</tt></a> program </li>
 </ul>
+<p>
+ (Multicall configuration)
+</p>
+<ul>
+ <li> The <a href="s6-portable-utils.html"><tt>s6-portable-utils</tt></a> multicall binary </li>
+</ul>
 
 <h2> Related resources </h2>
 
diff --git a/doc/s6-portable-utils.html b/doc/s6-portable-utils.html
new file mode 100644
index 0000000..5700f38
--- /dev/null
+++ b/doc/s6-portable-utils.html
@@ -0,0 +1,53 @@
+<html>
+  <head>
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+    <meta http-equiv="Content-Language" content="en" />
+    <title>s6-portable-utils: the s6-portable-utils multicall binary</title>
+    <meta name="Description" content="s6-portable-utils: the s6-portable-utils multicall binary" />
+    <meta name="Keywords" content="s6-portable-utils command multicall" />
+    <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
+  </head>
+<body>
+
+<p>
+<a href="index.html">s6-portable-utils</a><br />
+<a href="//skarnet.org/software/">Software</a><br />
+<a href="//skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>s6-portable-utils</tt> multicall binary </h1>
+
+<p>
+The <tt>s6-portable-utils</tt> program is only available when the
+<tt>--enable-multicall</tt> option has been given to the <tt>configure</tt>
+program at build time. In this configuration, <tt>s6-portable-utils</tt> is
+a multicall binary implementing the functionality of <em>all</em>
+the programs in the s6-portable-utils package; and the other programs, instead
+of being executables of their own, are symbolic links to the
+s6-portable-utils binary.
+</p>
+
+<h2> Interface </h2>
+
+<pre>
+     s6-portable-utils <em>subcommand</em> <em>subcommand_arguments...</em>
+</pre>
+
+<p>
+ s6-portable-utils will run the <em>subcommand</em> will its arguments. For
+instance, <tt>s6-portable-utils s6-seq 1 10</tt> will run the equivalent of the
+<a href="s6-seq.html">program</a>, so this command will list natural numbers
+from 1 to 10.
+</p>
+
+<p>
+ Alternatively, if s6-portable-utils is called with the name of an existing
+command, it will run the equivalent of that command. For instance,
+if the <tt>/usr/bin/s6-seq</tt> file is a (hard or symbolic) link to
+the <tt>s6-portable-utils</tt> binary, <tt>/usr/bin/s6-seq 1 10</tt> will list
+natural numbers from 1 to 10.
+</p>
+
+</body>
+</html>
diff --git a/package/deps.mak b/package/deps.mak
index 3593d61..d4e6bf6 100644
--- a/package/deps.mak
+++ b/package/deps.mak
@@ -2,133 +2,135 @@
 # This file has been generated by tools/gen-deps.sh
 #
 
-src/skaembutils/s6-basename.o src/skaembutils/s6-basename.lo: src/skaembutils/s6-basename.c
-src/skaembutils/s6-cat.o src/skaembutils/s6-cat.lo: src/skaembutils/s6-cat.c
-src/skaembutils/s6-chmod.o src/skaembutils/s6-chmod.lo: src/skaembutils/s6-chmod.c
-src/skaembutils/s6-chown.o src/skaembutils/s6-chown.lo: src/skaembutils/s6-chown.c
-src/skaembutils/s6-clock.o src/skaembutils/s6-clock.lo: src/skaembutils/s6-clock.c
-src/skaembutils/s6-cut.o src/skaembutils/s6-cut.lo: src/skaembutils/s6-cut.c
-src/skaembutils/s6-dirname.o src/skaembutils/s6-dirname.lo: src/skaembutils/s6-dirname.c
-src/skaembutils/s6-dumpenv.o src/skaembutils/s6-dumpenv.lo: src/skaembutils/s6-dumpenv.c
-src/skaembutils/s6-echo.o src/skaembutils/s6-echo.lo: src/skaembutils/s6-echo.c
-src/skaembutils/s6-env.o src/skaembutils/s6-env.lo: src/skaembutils/s6-env.c src/include/s6-portable-utils/config.h
-src/skaembutils/s6-expr.o src/skaembutils/s6-expr.lo: src/skaembutils/s6-expr.c
-src/skaembutils/s6-false.o src/skaembutils/s6-false.lo: src/skaembutils/s6-false.c
-src/skaembutils/s6-format-filter.o src/skaembutils/s6-format-filter.lo: src/skaembutils/s6-format-filter.c
-src/skaembutils/s6-grep.o src/skaembutils/s6-grep.lo: src/skaembutils/s6-grep.c
-src/skaembutils/s6-head.o src/skaembutils/s6-head.lo: src/skaembutils/s6-head.c
-src/skaembutils/s6-hiercopy.o src/skaembutils/s6-hiercopy.lo: src/skaembutils/s6-hiercopy.c
-src/skaembutils/s6-linkname.o src/skaembutils/s6-linkname.lo: src/skaembutils/s6-linkname.c
-src/skaembutils/s6-ln.o src/skaembutils/s6-ln.lo: src/skaembutils/s6-ln.c
-src/skaembutils/s6-ls.o src/skaembutils/s6-ls.lo: src/skaembutils/s6-ls.c
-src/skaembutils/s6-maximumtime.o src/skaembutils/s6-maximumtime.lo: src/skaembutils/s6-maximumtime.c
-src/skaembutils/s6-mkdir.o src/skaembutils/s6-mkdir.lo: src/skaembutils/s6-mkdir.c
-src/skaembutils/s6-mkfifo.o src/skaembutils/s6-mkfifo.lo: src/skaembutils/s6-mkfifo.c
-src/skaembutils/s6-nice.o src/skaembutils/s6-nice.lo: src/skaembutils/s6-nice.c
-src/skaembutils/s6-nuke.o src/skaembutils/s6-nuke.lo: src/skaembutils/s6-nuke.c
-src/skaembutils/s6-pause.o src/skaembutils/s6-pause.lo: src/skaembutils/s6-pause.c
-src/skaembutils/s6-printenv.o src/skaembutils/s6-printenv.lo: src/skaembutils/s6-printenv.c
-src/skaembutils/s6-quote-filter.o src/skaembutils/s6-quote-filter.lo: src/skaembutils/s6-quote-filter.c
-src/skaembutils/s6-quote.o src/skaembutils/s6-quote.lo: src/skaembutils/s6-quote.c
-src/skaembutils/s6-rename.o src/skaembutils/s6-rename.lo: src/skaembutils/s6-rename.c
-src/skaembutils/s6-rmrf.o src/skaembutils/s6-rmrf.lo: src/skaembutils/s6-rmrf.c
-src/skaembutils/s6-seq.o src/skaembutils/s6-seq.lo: src/skaembutils/s6-seq.c
-src/skaembutils/s6-sleep.o src/skaembutils/s6-sleep.lo: src/skaembutils/s6-sleep.c
-src/skaembutils/s6-sort.o src/skaembutils/s6-sort.lo: src/skaembutils/s6-sort.c
-src/skaembutils/s6-sync.o src/skaembutils/s6-sync.lo: src/skaembutils/s6-sync.c
-src/skaembutils/s6-tai64ndiff.o src/skaembutils/s6-tai64ndiff.lo: src/skaembutils/s6-tai64ndiff.c
-src/skaembutils/s6-tail.o src/skaembutils/s6-tail.lo: src/skaembutils/s6-tail.c
-src/skaembutils/s6-touch.o src/skaembutils/s6-touch.lo: src/skaembutils/s6-touch.c
-src/skaembutils/s6-true.o src/skaembutils/s6-true.lo: src/skaembutils/s6-true.c
-src/skaembutils/s6-uniquename.o src/skaembutils/s6-uniquename.lo: src/skaembutils/s6-uniquename.c
-src/skaembutils/s6-unquote-filter.o src/skaembutils/s6-unquote-filter.lo: src/skaembutils/s6-unquote-filter.c
-src/skaembutils/s6-unquote.o src/skaembutils/s6-unquote.lo: src/skaembutils/s6-unquote.c
-src/skaembutils/s6-update-symlinks.o src/skaembutils/s6-update-symlinks.lo: src/skaembutils/s6-update-symlinks.c
-src/skaembutils/seekablepipe.o src/skaembutils/seekablepipe.lo: src/skaembutils/seekablepipe.c
+src/s6-portable-utils/s6-basename.o src/s6-portable-utils/s6-basename.lo: src/s6-portable-utils/s6-basename.c
+src/s6-portable-utils/s6-cat.o src/s6-portable-utils/s6-cat.lo: src/s6-portable-utils/s6-cat.c
+src/s6-portable-utils/s6-chmod.o src/s6-portable-utils/s6-chmod.lo: src/s6-portable-utils/s6-chmod.c
+src/s6-portable-utils/s6-chown.o src/s6-portable-utils/s6-chown.lo: src/s6-portable-utils/s6-chown.c
+src/s6-portable-utils/s6-clock.o src/s6-portable-utils/s6-clock.lo: src/s6-portable-utils/s6-clock.c
+src/s6-portable-utils/s6-cut.o src/s6-portable-utils/s6-cut.lo: src/s6-portable-utils/s6-cut.c
+src/s6-portable-utils/s6-dirname.o src/s6-portable-utils/s6-dirname.lo: src/s6-portable-utils/s6-dirname.c
+src/s6-portable-utils/s6-dumpenv.o src/s6-portable-utils/s6-dumpenv.lo: src/s6-portable-utils/s6-dumpenv.c
+src/s6-portable-utils/s6-echo.o src/s6-portable-utils/s6-echo.lo: src/s6-portable-utils/s6-echo.c
+src/s6-portable-utils/s6-env.o src/s6-portable-utils/s6-env.lo: src/s6-portable-utils/s6-env.c src/include/s6-portable-utils/config.h
+src/s6-portable-utils/s6-expr.o src/s6-portable-utils/s6-expr.lo: src/s6-portable-utils/s6-expr.c
+src/s6-portable-utils/s6-false.o src/s6-portable-utils/s6-false.lo: src/s6-portable-utils/s6-false.c
+src/s6-portable-utils/s6-format-filter.o src/s6-portable-utils/s6-format-filter.lo: src/s6-portable-utils/s6-format-filter.c
+src/s6-portable-utils/s6-grep.o src/s6-portable-utils/s6-grep.lo: src/s6-portable-utils/s6-grep.c
+src/s6-portable-utils/s6-head.o src/s6-portable-utils/s6-head.lo: src/s6-portable-utils/s6-head.c
+src/s6-portable-utils/s6-hiercopy.o src/s6-portable-utils/s6-hiercopy.lo: src/s6-portable-utils/s6-hiercopy.c
+src/s6-portable-utils/s6-linkname.o src/s6-portable-utils/s6-linkname.lo: src/s6-portable-utils/s6-linkname.c
+src/s6-portable-utils/s6-ln.o src/s6-portable-utils/s6-ln.lo: src/s6-portable-utils/s6-ln.c
+src/s6-portable-utils/s6-ls.o src/s6-portable-utils/s6-ls.lo: src/s6-portable-utils/s6-ls.c
+src/s6-portable-utils/s6-maximumtime.o src/s6-portable-utils/s6-maximumtime.lo: src/s6-portable-utils/s6-maximumtime.c
+src/s6-portable-utils/s6-mkdir.o src/s6-portable-utils/s6-mkdir.lo: src/s6-portable-utils/s6-mkdir.c
+src/s6-portable-utils/s6-mkfifo.o src/s6-portable-utils/s6-mkfifo.lo: src/s6-portable-utils/s6-mkfifo.c
+src/s6-portable-utils/s6-nice.o src/s6-portable-utils/s6-nice.lo: src/s6-portable-utils/s6-nice.c
+src/s6-portable-utils/s6-nuke.o src/s6-portable-utils/s6-nuke.lo: src/s6-portable-utils/s6-nuke.c
+src/s6-portable-utils/s6-pause.o src/s6-portable-utils/s6-pause.lo: src/s6-portable-utils/s6-pause.c
+src/s6-portable-utils/s6-printenv.o src/s6-portable-utils/s6-printenv.lo: src/s6-portable-utils/s6-printenv.c
+src/s6-portable-utils/s6-quote-filter.o src/s6-portable-utils/s6-quote-filter.lo: src/s6-portable-utils/s6-quote-filter.c
+src/s6-portable-utils/s6-quote.o src/s6-portable-utils/s6-quote.lo: src/s6-portable-utils/s6-quote.c
+src/s6-portable-utils/s6-rename.o src/s6-portable-utils/s6-rename.lo: src/s6-portable-utils/s6-rename.c
+src/s6-portable-utils/s6-rmrf.o src/s6-portable-utils/s6-rmrf.lo: src/s6-portable-utils/s6-rmrf.c
+src/s6-portable-utils/s6-seq.o src/s6-portable-utils/s6-seq.lo: src/s6-portable-utils/s6-seq.c
+src/s6-portable-utils/s6-sleep.o src/s6-portable-utils/s6-sleep.lo: src/s6-portable-utils/s6-sleep.c
+src/s6-portable-utils/s6-sort.o src/s6-portable-utils/s6-sort.lo: src/s6-portable-utils/s6-sort.c
+src/s6-portable-utils/s6-sync.o src/s6-portable-utils/s6-sync.lo: src/s6-portable-utils/s6-sync.c
+src/s6-portable-utils/s6-tai64ndiff.o src/s6-portable-utils/s6-tai64ndiff.lo: src/s6-portable-utils/s6-tai64ndiff.c
+src/s6-portable-utils/s6-tail.o src/s6-portable-utils/s6-tail.lo: src/s6-portable-utils/s6-tail.c
+src/s6-portable-utils/s6-touch.o src/s6-portable-utils/s6-touch.lo: src/s6-portable-utils/s6-touch.c
+src/s6-portable-utils/s6-true.o src/s6-portable-utils/s6-true.lo: src/s6-portable-utils/s6-true.c
+src/s6-portable-utils/s6-uniquename.o src/s6-portable-utils/s6-uniquename.lo: src/s6-portable-utils/s6-uniquename.c
+src/s6-portable-utils/s6-unquote-filter.o src/s6-portable-utils/s6-unquote-filter.lo: src/s6-portable-utils/s6-unquote-filter.c
+src/s6-portable-utils/s6-unquote.o src/s6-portable-utils/s6-unquote.lo: src/s6-portable-utils/s6-unquote.c
+src/s6-portable-utils/s6-update-symlinks.o src/s6-portable-utils/s6-update-symlinks.lo: src/s6-portable-utils/s6-update-symlinks.c
+src/s6-portable-utils/seekablepipe.o src/s6-portable-utils/seekablepipe.lo: src/s6-portable-utils/seekablepipe.c
 
+s6-portable-utils: EXTRA_LIBS := -lskarnet ${SPAWN_LIB}
+s6-portable-utils: src/multicall/s6-portable-utils.o
 s6-basename: EXTRA_LIBS := -lskarnet
-s6-basename: src/skaembutils/s6-basename.o
+s6-basename: src/s6-portable-utils/s6-basename.o
 s6-cat: EXTRA_LIBS := -lskarnet
-s6-cat: src/skaembutils/s6-cat.o
+s6-cat: src/s6-portable-utils/s6-cat.o
 s6-chmod: EXTRA_LIBS := -lskarnet
-s6-chmod: src/skaembutils/s6-chmod.o
+s6-chmod: src/s6-portable-utils/s6-chmod.o
 s6-chown: EXTRA_LIBS := -lskarnet
-s6-chown: src/skaembutils/s6-chown.o
+s6-chown: src/s6-portable-utils/s6-chown.o
 s6-clock: EXTRA_LIBS := -lskarnet ${SYSCLOCK_LIB}
-s6-clock: src/skaembutils/s6-clock.o
+s6-clock: src/s6-portable-utils/s6-clock.o
 s6-cut: EXTRA_LIBS := -lskarnet
-s6-cut: src/skaembutils/s6-cut.o
+s6-cut: src/s6-portable-utils/s6-cut.o
 s6-dirname: EXTRA_LIBS := -lskarnet
-s6-dirname: src/skaembutils/s6-dirname.o
+s6-dirname: src/s6-portable-utils/s6-dirname.o
 s6-dumpenv: EXTRA_LIBS := -lskarnet
-s6-dumpenv: src/skaembutils/s6-dumpenv.o
+s6-dumpenv: src/s6-portable-utils/s6-dumpenv.o
 s6-echo: EXTRA_LIBS := -lskarnet
-s6-echo: src/skaembutils/s6-echo.o
+s6-echo: src/s6-portable-utils/s6-echo.o
 s6-env: EXTRA_LIBS := -lskarnet
-s6-env: src/skaembutils/s6-env.o
+s6-env: src/s6-portable-utils/s6-env.o
 s6-expr: EXTRA_LIBS := -lskarnet
-s6-expr: src/skaembutils/s6-expr.o
+s6-expr: src/s6-portable-utils/s6-expr.o
 s6-false: EXTRA_LIBS :=
-s6-false: src/skaembutils/s6-false.o
+s6-false: src/s6-portable-utils/s6-false.o
 s6-format-filter: EXTRA_LIBS := -lskarnet
-s6-format-filter: src/skaembutils/s6-format-filter.o
+s6-format-filter: src/s6-portable-utils/s6-format-filter.o
 s6-grep: EXTRA_LIBS := -lskarnet
-s6-grep: src/skaembutils/s6-grep.o
+s6-grep: src/s6-portable-utils/s6-grep.o
 s6-head: EXTRA_LIBS := -lskarnet
-s6-head: src/skaembutils/s6-head.o
+s6-head: src/s6-portable-utils/s6-head.o
 s6-hiercopy: EXTRA_LIBS := -lskarnet
-s6-hiercopy: src/skaembutils/s6-hiercopy.o
+s6-hiercopy: src/s6-portable-utils/s6-hiercopy.o
 s6-linkname: EXTRA_LIBS := -lskarnet
-s6-linkname: src/skaembutils/s6-linkname.o
-s6-ln: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB}
-s6-ln: src/skaembutils/s6-ln.o
+s6-linkname: src/s6-portable-utils/s6-linkname.o
+s6-ln: EXTRA_LIBS := -lskarnet
+s6-ln: src/s6-portable-utils/s6-ln.o
 s6-ls: EXTRA_LIBS := -lskarnet
-s6-ls: src/skaembutils/s6-ls.o
+s6-ls: src/s6-portable-utils/s6-ls.o
 s6-maximumtime: EXTRA_LIBS := -lskarnet ${SYSCLOCK_LIB} ${SPAWN_LIB}
-s6-maximumtime: src/skaembutils/s6-maximumtime.o
+s6-maximumtime: src/s6-portable-utils/s6-maximumtime.o
 s6-mkdir: EXTRA_LIBS := -lskarnet
-s6-mkdir: src/skaembutils/s6-mkdir.o
+s6-mkdir: src/s6-portable-utils/s6-mkdir.o
 s6-mkfifo: EXTRA_LIBS := -lskarnet
-s6-mkfifo: src/skaembutils/s6-mkfifo.o
+s6-mkfifo: src/s6-portable-utils/s6-mkfifo.o
 s6-nice: EXTRA_LIBS := -lskarnet
-s6-nice: src/skaembutils/s6-nice.o
+s6-nice: src/s6-portable-utils/s6-nice.o
 s6-nuke: EXTRA_LIBS := -lskarnet
-s6-nuke: src/skaembutils/s6-nuke.o
+s6-nuke: src/s6-portable-utils/s6-nuke.o
 s6-pause: EXTRA_LIBS := -lskarnet
-s6-pause: src/skaembutils/s6-pause.o
+s6-pause: src/s6-portable-utils/s6-pause.o
 s6-printenv: EXTRA_LIBS := -lskarnet
-s6-printenv: src/skaembutils/s6-printenv.o
+s6-printenv: src/s6-portable-utils/s6-printenv.o
 s6-quote: EXTRA_LIBS := -lskarnet
-s6-quote: src/skaembutils/s6-quote.o
+s6-quote: src/s6-portable-utils/s6-quote.o
 s6-quote-filter: EXTRA_LIBS := -lskarnet
-s6-quote-filter: src/skaembutils/s6-quote-filter.o
+s6-quote-filter: src/s6-portable-utils/s6-quote-filter.o
 s6-rename: EXTRA_LIBS := -lskarnet
-s6-rename: src/skaembutils/s6-rename.o
+s6-rename: src/s6-portable-utils/s6-rename.o
 s6-rmrf: EXTRA_LIBS := -lskarnet
-s6-rmrf: src/skaembutils/s6-rmrf.o
+s6-rmrf: src/s6-portable-utils/s6-rmrf.o
 s6-seq: EXTRA_LIBS := -lskarnet
-s6-seq: src/skaembutils/s6-seq.o
+s6-seq: src/s6-portable-utils/s6-seq.o
 s6-sleep: EXTRA_LIBS := -lskarnet ${SYSCLOCK_LIB}
-s6-sleep: src/skaembutils/s6-sleep.o
+s6-sleep: src/s6-portable-utils/s6-sleep.o
 s6-sort: EXTRA_LIBS := -lskarnet
-s6-sort: src/skaembutils/s6-sort.o
+s6-sort: src/s6-portable-utils/s6-sort.o
 s6-sync: EXTRA_LIBS :=
-s6-sync: src/skaembutils/s6-sync.o
+s6-sync: src/s6-portable-utils/s6-sync.o
 s6-tai64ndiff: EXTRA_LIBS := -lskarnet
-s6-tai64ndiff: src/skaembutils/s6-tai64ndiff.o
+s6-tai64ndiff: src/s6-portable-utils/s6-tai64ndiff.o
 s6-tail: EXTRA_LIBS := -lskarnet
-s6-tail: src/skaembutils/s6-tail.o
+s6-tail: src/s6-portable-utils/s6-tail.o
 s6-touch: EXTRA_LIBS := -lskarnet
-s6-touch: src/skaembutils/s6-touch.o
+s6-touch: src/s6-portable-utils/s6-touch.o
 s6-true: EXTRA_LIBS :=
-s6-true: src/skaembutils/s6-true.o
-s6-uniquename: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB}
-s6-uniquename: src/skaembutils/s6-uniquename.o
+s6-true: src/s6-portable-utils/s6-true.o
+s6-uniquename: EXTRA_LIBS := -lskarnet
+s6-uniquename: src/s6-portable-utils/s6-uniquename.o
 s6-unquote: EXTRA_LIBS := -lskarnet
-s6-unquote: src/skaembutils/s6-unquote.o
+s6-unquote: src/s6-portable-utils/s6-unquote.o
 s6-unquote-filter: EXTRA_LIBS := -lskarnet
-s6-unquote-filter: src/skaembutils/s6-unquote-filter.o
-s6-update-symlinks: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB}
-s6-update-symlinks: src/skaembutils/s6-update-symlinks.o
+s6-unquote-filter: src/s6-portable-utils/s6-unquote-filter.o
+s6-update-symlinks: EXTRA_LIBS := -lskarnet
+s6-update-symlinks: src/s6-portable-utils/s6-update-symlinks.o
 seekablepipe: EXTRA_LIBS := -lskarnet
-seekablepipe: src/skaembutils/seekablepipe.o
+seekablepipe: src/s6-portable-utils/seekablepipe.o
diff --git a/package/modes b/package/modes
index cd33bbd..d45eecf 100644
--- a/package/modes
+++ b/package/modes
@@ -41,3 +41,4 @@ s6-unquote		0755
 s6-unquote-filter	0755
 s6-update-symlinks	0755
 seekablepipe		0755
+s6-portable-utils	0755
diff --git a/package/targets.mak b/package/targets.mak
index b8c613e..6403a9b 100644
--- a/package/targets.mak
+++ b/package/targets.mak
@@ -1,46 +1,24 @@
-BIN_TARGETS := \
-s6-basename \
-s6-cat \
-s6-chmod \
-s6-chown \
-s6-clock \
-s6-cut \
-s6-dirname \
-s6-dumpenv \
-s6-echo \
-s6-env \
-s6-expr \
-s6-false \
-s6-format-filter \
-s6-grep \
-s6-head \
-s6-hiercopy \
-s6-linkname \
-s6-ln \
-s6-ls \
-s6-maximumtime \
-s6-mkdir \
-s6-mkfifo \
-s6-nice \
-s6-nuke \
-s6-pause \
-s6-printenv \
-s6-quote \
-s6-quote-filter \
-s6-rename \
-s6-rmrf \
-s6-seq \
-s6-sleep \
-s6-sort \
-s6-sync \
-s6-tai64ndiff \
-s6-tail \
-s6-touch \
-s6-true \
-s6-uniquename \
-s6-unquote \
-s6-unquote-filter \
-s6-update-symlinks \
-seekablepipe
-
 LIBEXEC_TARGETS :=
+
+ifeq ($(MULTICALL),1)
+
+BIN_TARGETS := $(package)
+BIN_SYMLINKS := $(notdir $(wildcard src/$(package)/deps-exe/*))
+EXTRA_TEMP := src/multicall/$(package).c
+
+define symlink_definition
+SYMLINK_TARGET_$(1) := $(package)
+endef
+$(foreach name,$(BIN_SYMLINKS),$(eval $(call symlink_definition,$(name))))
+
+src/multicall/$(package).c: tools/gen-multicall.sh src/$(package)/deps-exe
+	./tools/gen-multicall.sh $(package) > src/multicall/$(package).c
+
+src/multicall/$(package).o: src/multicall/$(package).c src/include/$(package)/config.h
+
+else
+
+BIN_TARGETS := $(notdir $(wildcard src/$(package)/deps-exe/*))
+BIN_SYMLINKS :=
+
+endif
diff --git a/src/multicall/deps-exe/s6-portable-utils b/src/multicall/deps-exe/s6-portable-utils
new file mode 100644
index 0000000..39597dd
--- /dev/null
+++ b/src/multicall/deps-exe/s6-portable-utils
@@ -0,0 +1,2 @@
+-lskarnet
+${SPAWN_LIB}
diff --git a/src/skaembutils/deps-exe/s6-basename b/src/s6-portable-utils/deps-exe/s6-basename
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-basename
+++ b/src/s6-portable-utils/deps-exe/s6-basename
diff --git a/src/skaembutils/deps-exe/s6-cat b/src/s6-portable-utils/deps-exe/s6-cat
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-cat
+++ b/src/s6-portable-utils/deps-exe/s6-cat
diff --git a/src/skaembutils/deps-exe/s6-chmod b/src/s6-portable-utils/deps-exe/s6-chmod
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-chmod
+++ b/src/s6-portable-utils/deps-exe/s6-chmod
diff --git a/src/skaembutils/deps-exe/s6-chown b/src/s6-portable-utils/deps-exe/s6-chown
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-chown
+++ b/src/s6-portable-utils/deps-exe/s6-chown
diff --git a/src/skaembutils/deps-exe/s6-clock b/src/s6-portable-utils/deps-exe/s6-clock
index a11a5f4..a11a5f4 100644
--- a/src/skaembutils/deps-exe/s6-clock
+++ b/src/s6-portable-utils/deps-exe/s6-clock
diff --git a/src/skaembutils/deps-exe/s6-cut b/src/s6-portable-utils/deps-exe/s6-cut
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-cut
+++ b/src/s6-portable-utils/deps-exe/s6-cut
diff --git a/src/skaembutils/deps-exe/s6-dirname b/src/s6-portable-utils/deps-exe/s6-dirname
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-dirname
+++ b/src/s6-portable-utils/deps-exe/s6-dirname
diff --git a/src/skaembutils/deps-exe/s6-dumpenv b/src/s6-portable-utils/deps-exe/s6-dumpenv
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-dumpenv
+++ b/src/s6-portable-utils/deps-exe/s6-dumpenv
diff --git a/src/skaembutils/deps-exe/s6-echo b/src/s6-portable-utils/deps-exe/s6-echo
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-echo
+++ b/src/s6-portable-utils/deps-exe/s6-echo
diff --git a/src/skaembutils/deps-exe/s6-env b/src/s6-portable-utils/deps-exe/s6-env
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-env
+++ b/src/s6-portable-utils/deps-exe/s6-env
diff --git a/src/skaembutils/deps-exe/s6-expr b/src/s6-portable-utils/deps-exe/s6-expr
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-expr
+++ b/src/s6-portable-utils/deps-exe/s6-expr
diff --git a/src/skaembutils/deps-exe/s6-false b/src/s6-portable-utils/deps-exe/s6-false
index e69de29..e69de29 100644
--- a/src/skaembutils/deps-exe/s6-false
+++ b/src/s6-portable-utils/deps-exe/s6-false
diff --git a/src/skaembutils/deps-exe/s6-format-filter b/src/s6-portable-utils/deps-exe/s6-format-filter
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-format-filter
+++ b/src/s6-portable-utils/deps-exe/s6-format-filter
diff --git a/src/skaembutils/deps-exe/s6-grep b/src/s6-portable-utils/deps-exe/s6-grep
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-grep
+++ b/src/s6-portable-utils/deps-exe/s6-grep
diff --git a/src/skaembutils/deps-exe/s6-head b/src/s6-portable-utils/deps-exe/s6-head
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-head
+++ b/src/s6-portable-utils/deps-exe/s6-head
diff --git a/src/skaembutils/deps-exe/s6-hiercopy b/src/s6-portable-utils/deps-exe/s6-hiercopy
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-hiercopy
+++ b/src/s6-portable-utils/deps-exe/s6-hiercopy
diff --git a/src/skaembutils/deps-exe/s6-linkname b/src/s6-portable-utils/deps-exe/s6-linkname
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-linkname
+++ b/src/s6-portable-utils/deps-exe/s6-linkname
diff --git a/src/skaembutils/deps-exe/s6-ls b/src/s6-portable-utils/deps-exe/s6-ln
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-ls
+++ b/src/s6-portable-utils/deps-exe/s6-ln
diff --git a/src/skaembutils/deps-exe/s6-mkdir b/src/s6-portable-utils/deps-exe/s6-ls
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-mkdir
+++ b/src/s6-portable-utils/deps-exe/s6-ls
diff --git a/src/skaembutils/deps-exe/s6-maximumtime b/src/s6-portable-utils/deps-exe/s6-maximumtime
index 756dcc2..756dcc2 100644
--- a/src/skaembutils/deps-exe/s6-maximumtime
+++ b/src/s6-portable-utils/deps-exe/s6-maximumtime
diff --git a/src/skaembutils/deps-exe/s6-mkfifo b/src/s6-portable-utils/deps-exe/s6-mkdir
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-mkfifo
+++ b/src/s6-portable-utils/deps-exe/s6-mkdir
diff --git a/src/skaembutils/deps-exe/s6-nice b/src/s6-portable-utils/deps-exe/s6-mkfifo
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-nice
+++ b/src/s6-portable-utils/deps-exe/s6-mkfifo
diff --git a/src/skaembutils/deps-exe/s6-nuke b/src/s6-portable-utils/deps-exe/s6-nice
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-nuke
+++ b/src/s6-portable-utils/deps-exe/s6-nice
diff --git a/src/skaembutils/deps-exe/s6-pause b/src/s6-portable-utils/deps-exe/s6-nuke
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-pause
+++ b/src/s6-portable-utils/deps-exe/s6-nuke
diff --git a/src/skaembutils/deps-exe/s6-printenv b/src/s6-portable-utils/deps-exe/s6-pause
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-printenv
+++ b/src/s6-portable-utils/deps-exe/s6-pause
diff --git a/src/skaembutils/deps-exe/s6-quote b/src/s6-portable-utils/deps-exe/s6-printenv
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-quote
+++ b/src/s6-portable-utils/deps-exe/s6-printenv
diff --git a/src/skaembutils/deps-exe/s6-quote-filter b/src/s6-portable-utils/deps-exe/s6-quote
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-quote-filter
+++ b/src/s6-portable-utils/deps-exe/s6-quote
diff --git a/src/skaembutils/deps-exe/s6-rename b/src/s6-portable-utils/deps-exe/s6-quote-filter
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-rename
+++ b/src/s6-portable-utils/deps-exe/s6-quote-filter
diff --git a/src/skaembutils/deps-exe/s6-rmrf b/src/s6-portable-utils/deps-exe/s6-rename
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-rmrf
+++ b/src/s6-portable-utils/deps-exe/s6-rename
diff --git a/src/skaembutils/deps-exe/s6-seq b/src/s6-portable-utils/deps-exe/s6-rmrf
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-seq
+++ b/src/s6-portable-utils/deps-exe/s6-rmrf
diff --git a/src/skaembutils/deps-exe/s6-sort b/src/s6-portable-utils/deps-exe/s6-seq
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-sort
+++ b/src/s6-portable-utils/deps-exe/s6-seq
diff --git a/src/skaembutils/deps-exe/s6-sleep b/src/s6-portable-utils/deps-exe/s6-sleep
index a11a5f4..a11a5f4 100644
--- a/src/skaembutils/deps-exe/s6-sleep
+++ b/src/s6-portable-utils/deps-exe/s6-sleep
diff --git a/src/skaembutils/deps-exe/s6-tai64ndiff b/src/s6-portable-utils/deps-exe/s6-sort
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-tai64ndiff
+++ b/src/s6-portable-utils/deps-exe/s6-sort
diff --git a/src/skaembutils/deps-exe/s6-sync b/src/s6-portable-utils/deps-exe/s6-sync
index e69de29..e69de29 100644
--- a/src/skaembutils/deps-exe/s6-sync
+++ b/src/s6-portable-utils/deps-exe/s6-sync
diff --git a/src/skaembutils/deps-exe/s6-tail b/src/s6-portable-utils/deps-exe/s6-tai64ndiff
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-tail
+++ b/src/s6-portable-utils/deps-exe/s6-tai64ndiff
diff --git a/src/skaembutils/deps-exe/s6-touch b/src/s6-portable-utils/deps-exe/s6-tail
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-touch
+++ b/src/s6-portable-utils/deps-exe/s6-tail
diff --git a/src/skaembutils/deps-exe/s6-unquote b/src/s6-portable-utils/deps-exe/s6-touch
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-unquote
+++ b/src/s6-portable-utils/deps-exe/s6-touch
diff --git a/src/skaembutils/deps-exe/s6-true b/src/s6-portable-utils/deps-exe/s6-true
index e69de29..e69de29 100644
--- a/src/skaembutils/deps-exe/s6-true
+++ b/src/s6-portable-utils/deps-exe/s6-true
diff --git a/src/skaembutils/deps-exe/s6-unquote-filter b/src/s6-portable-utils/deps-exe/s6-uniquename
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/s6-unquote-filter
+++ b/src/s6-portable-utils/deps-exe/s6-uniquename
diff --git a/src/skaembutils/deps-exe/seekablepipe b/src/s6-portable-utils/deps-exe/s6-unquote
index e7187fe..e7187fe 100644
--- a/src/skaembutils/deps-exe/seekablepipe
+++ b/src/s6-portable-utils/deps-exe/s6-unquote
diff --git a/src/s6-portable-utils/deps-exe/s6-unquote-filter b/src/s6-portable-utils/deps-exe/s6-unquote-filter
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/s6-portable-utils/deps-exe/s6-unquote-filter
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/s6-portable-utils/deps-exe/s6-update-symlinks b/src/s6-portable-utils/deps-exe/s6-update-symlinks
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/s6-portable-utils/deps-exe/s6-update-symlinks
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/s6-portable-utils/deps-exe/seekablepipe b/src/s6-portable-utils/deps-exe/seekablepipe
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/s6-portable-utils/deps-exe/seekablepipe
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/skaembutils/s6-basename.c b/src/s6-portable-utils/s6-basename.c
index 9e00763..9e00763 100644
--- a/src/skaembutils/s6-basename.c
+++ b/src/s6-portable-utils/s6-basename.c
diff --git a/src/skaembutils/s6-cat.c b/src/s6-portable-utils/s6-cat.c
index 8ff5a47..8ff5a47 100644
--- a/src/skaembutils/s6-cat.c
+++ b/src/s6-portable-utils/s6-cat.c
diff --git a/src/skaembutils/s6-chmod.c b/src/s6-portable-utils/s6-chmod.c
index 946a318..946a318 100644
--- a/src/skaembutils/s6-chmod.c
+++ b/src/s6-portable-utils/s6-chmod.c
diff --git a/src/skaembutils/s6-chown.c b/src/s6-portable-utils/s6-chown.c
index 77cd94b..77cd94b 100644
--- a/src/skaembutils/s6-chown.c
+++ b/src/s6-portable-utils/s6-chown.c
diff --git a/src/skaembutils/s6-clock.c b/src/s6-portable-utils/s6-clock.c
index 5a9e67a..5a9e67a 100644
--- a/src/skaembutils/s6-clock.c
+++ b/src/s6-portable-utils/s6-clock.c
diff --git a/src/skaembutils/s6-cut.c b/src/s6-portable-utils/s6-cut.c
index e55907b..e55907b 100644
--- a/src/skaembutils/s6-cut.c
+++ b/src/s6-portable-utils/s6-cut.c
diff --git a/src/skaembutils/s6-dirname.c b/src/s6-portable-utils/s6-dirname.c
index 83782ac..83782ac 100644
--- a/src/skaembutils/s6-dirname.c
+++ b/src/s6-portable-utils/s6-dirname.c
diff --git a/src/skaembutils/s6-dumpenv.c b/src/s6-portable-utils/s6-dumpenv.c
index 4b79170..4b79170 100644
--- a/src/skaembutils/s6-dumpenv.c
+++ b/src/s6-portable-utils/s6-dumpenv.c
diff --git a/src/skaembutils/s6-echo.c b/src/s6-portable-utils/s6-echo.c
index 11427d2..11427d2 100644
--- a/src/skaembutils/s6-echo.c
+++ b/src/s6-portable-utils/s6-echo.c
diff --git a/src/skaembutils/s6-env.c b/src/s6-portable-utils/s6-env.c
index 01479ce..01479ce 100644
--- a/src/skaembutils/s6-env.c
+++ b/src/s6-portable-utils/s6-env.c
diff --git a/src/skaembutils/s6-expr.c b/src/s6-portable-utils/s6-expr.c
index 91be041..91be041 100644
--- a/src/skaembutils/s6-expr.c
+++ b/src/s6-portable-utils/s6-expr.c
diff --git a/src/skaembutils/s6-false.c b/src/s6-portable-utils/s6-false.c
index 75f483e..75f483e 100644
--- a/src/skaembutils/s6-false.c
+++ b/src/s6-portable-utils/s6-false.c
diff --git a/src/skaembutils/s6-format-filter.c b/src/s6-portable-utils/s6-format-filter.c
index 57b2869..57b2869 100644
--- a/src/skaembutils/s6-format-filter.c
+++ b/src/s6-portable-utils/s6-format-filter.c
diff --git a/src/skaembutils/s6-grep.c b/src/s6-portable-utils/s6-grep.c
index ebfa064..ebfa064 100644
--- a/src/skaembutils/s6-grep.c
+++ b/src/s6-portable-utils/s6-grep.c
diff --git a/src/skaembutils/s6-head.c b/src/s6-portable-utils/s6-head.c
index de7d937..de7d937 100644
--- a/src/skaembutils/s6-head.c
+++ b/src/s6-portable-utils/s6-head.c
diff --git a/src/skaembutils/s6-hiercopy.c b/src/s6-portable-utils/s6-hiercopy.c
index a57dd69..a57dd69 100644
--- a/src/skaembutils/s6-hiercopy.c
+++ b/src/s6-portable-utils/s6-hiercopy.c
diff --git a/src/skaembutils/s6-linkname.c b/src/s6-portable-utils/s6-linkname.c
index 8471ac4..8471ac4 100644
--- a/src/skaembutils/s6-linkname.c
+++ b/src/s6-portable-utils/s6-linkname.c
diff --git a/src/skaembutils/s6-ln.c b/src/s6-portable-utils/s6-ln.c
index b33562f..f0b0c5e 100644
--- a/src/skaembutils/s6-ln.c
+++ b/src/s6-portable-utils/s6-ln.c
@@ -21,7 +21,7 @@
 #include <skalibs/skamisc.h>
 
 #define USAGE "s6-ln [ -n ] [ -s ] [ -f ] [ -L ] [ -P ] src... dest"
-#define SUFFIX ":s6-ln:XXXXXX"
+#define LN_SUFFIX ":s6-ln:XXXXXX"
 
 #ifdef SKALIBS_HASLINKAT
 
@@ -53,9 +53,9 @@ static int ln_doit (char const *old, char const *new, link_func_ref mylink, int
     }
     {
       size_t newlen = strlen(new) ;
-      char fn[newlen + sizeof(SUFFIX)] ;
+      char fn[newlen + sizeof(LN_SUFFIX)] ;
       memcpy(fn, new, newlen) ;
-      memcpy(fn + newlen, SUFFIX, sizeof(SUFFIX)) ;
+      memcpy(fn + newlen, LN_SUFFIX, sizeof(LN_SUFFIX)) ;
       if (mklinktemp(old, fn, mylink) == -1)
       {
         strerr_warnwu3sys("make a link", " to ", old) ;
diff --git a/src/skaembutils/s6-ls.c b/src/s6-portable-utils/s6-ls.c
index b426da7..b426da7 100644
--- a/src/skaembutils/s6-ls.c
+++ b/src/s6-portable-utils/s6-ls.c
diff --git a/src/skaembutils/s6-maximumtime.c b/src/s6-portable-utils/s6-maximumtime.c
index 4da7e89..4da7e89 100644
--- a/src/skaembutils/s6-maximumtime.c
+++ b/src/s6-portable-utils/s6-maximumtime.c
diff --git a/src/skaembutils/s6-mkdir.c b/src/s6-portable-utils/s6-mkdir.c
index 8212813..8212813 100644
--- a/src/skaembutils/s6-mkdir.c
+++ b/src/s6-portable-utils/s6-mkdir.c
diff --git a/src/skaembutils/s6-mkfifo.c b/src/s6-portable-utils/s6-mkfifo.c
index 37e84af..37e84af 100644
--- a/src/skaembutils/s6-mkfifo.c
+++ b/src/s6-portable-utils/s6-mkfifo.c
diff --git a/src/skaembutils/s6-nice.c b/src/s6-portable-utils/s6-nice.c
index 38017f6..38017f6 100644
--- a/src/skaembutils/s6-nice.c
+++ b/src/s6-portable-utils/s6-nice.c
diff --git a/src/skaembutils/s6-nuke.c b/src/s6-portable-utils/s6-nuke.c
index ff80b6c..ff80b6c 100644
--- a/src/skaembutils/s6-nuke.c
+++ b/src/s6-portable-utils/s6-nuke.c
diff --git a/src/skaembutils/s6-pause.c b/src/s6-portable-utils/s6-pause.c
index e0416ba..e0416ba 100644
--- a/src/skaembutils/s6-pause.c
+++ b/src/s6-portable-utils/s6-pause.c
diff --git a/src/skaembutils/s6-printenv.c b/src/s6-portable-utils/s6-printenv.c
index d1a48de..d1a48de 100644
--- a/src/skaembutils/s6-printenv.c
+++ b/src/s6-portable-utils/s6-printenv.c
diff --git a/src/skaembutils/s6-quote-filter.c b/src/s6-portable-utils/s6-quote-filter.c
index 5ed5b60..5ed5b60 100644
--- a/src/skaembutils/s6-quote-filter.c
+++ b/src/s6-portable-utils/s6-quote-filter.c
diff --git a/src/skaembutils/s6-quote.c b/src/s6-portable-utils/s6-quote.c
index 74c137c..74c137c 100644
--- a/src/skaembutils/s6-quote.c
+++ b/src/s6-portable-utils/s6-quote.c
diff --git a/src/skaembutils/s6-rename.c b/src/s6-portable-utils/s6-rename.c
index a4fc1ee..a4fc1ee 100644
--- a/src/skaembutils/s6-rename.c
+++ b/src/s6-portable-utils/s6-rename.c
diff --git a/src/skaembutils/s6-rmrf.c b/src/s6-portable-utils/s6-rmrf.c
index ebbf9a0..ebbf9a0 100644
--- a/src/skaembutils/s6-rmrf.c
+++ b/src/s6-portable-utils/s6-rmrf.c
diff --git a/src/skaembutils/s6-seq.c b/src/s6-portable-utils/s6-seq.c
index a7b70ba..a7b70ba 100644
--- a/src/skaembutils/s6-seq.c
+++ b/src/s6-portable-utils/s6-seq.c
diff --git a/src/skaembutils/s6-sleep.c b/src/s6-portable-utils/s6-sleep.c
index c5a05b4..c5a05b4 100644
--- a/src/skaembutils/s6-sleep.c
+++ b/src/s6-portable-utils/s6-sleep.c
diff --git a/src/skaembutils/s6-sort.c b/src/s6-portable-utils/s6-sort.c
index 66955fe..66955fe 100644
--- a/src/skaembutils/s6-sort.c
+++ b/src/s6-portable-utils/s6-sort.c
diff --git a/src/skaembutils/s6-sync.c b/src/s6-portable-utils/s6-sync.c
index 34e3e73..34e3e73 100644
--- a/src/skaembutils/s6-sync.c
+++ b/src/s6-portable-utils/s6-sync.c
diff --git a/src/skaembutils/s6-tai64ndiff.c b/src/s6-portable-utils/s6-tai64ndiff.c
index cb35b4e..cb35b4e 100644
--- a/src/skaembutils/s6-tai64ndiff.c
+++ b/src/s6-portable-utils/s6-tai64ndiff.c
diff --git a/src/skaembutils/s6-tail.c b/src/s6-portable-utils/s6-tail.c
index a3091cd..a3091cd 100644
--- a/src/skaembutils/s6-tail.c
+++ b/src/s6-portable-utils/s6-tail.c
diff --git a/src/skaembutils/s6-touch.c b/src/s6-portable-utils/s6-touch.c
index 7c2c156..7c2c156 100644
--- a/src/skaembutils/s6-touch.c
+++ b/src/s6-portable-utils/s6-touch.c
diff --git a/src/skaembutils/s6-true.c b/src/s6-portable-utils/s6-true.c
index f372d43..f372d43 100644
--- a/src/skaembutils/s6-true.c
+++ b/src/s6-portable-utils/s6-true.c
diff --git a/src/skaembutils/s6-uniquename.c b/src/s6-portable-utils/s6-uniquename.c
index 479c50d..479c50d 100644
--- a/src/skaembutils/s6-uniquename.c
+++ b/src/s6-portable-utils/s6-uniquename.c
diff --git a/src/skaembutils/s6-unquote-filter.c b/src/s6-portable-utils/s6-unquote-filter.c
index 4b1b4ef..4b1b4ef 100644
--- a/src/skaembutils/s6-unquote-filter.c
+++ b/src/s6-portable-utils/s6-unquote-filter.c
diff --git a/src/skaembutils/s6-unquote.c b/src/s6-portable-utils/s6-unquote.c
index 34dcea5..34dcea5 100644
--- a/src/skaembutils/s6-unquote.c
+++ b/src/s6-portable-utils/s6-unquote.c
diff --git a/src/skaembutils/s6-update-symlinks.c b/src/s6-portable-utils/s6-update-symlinks.c
index 6b9a145..5ba9156 100644
--- a/src/skaembutils/s6-update-symlinks.c
+++ b/src/s6-portable-utils/s6-update-symlinks.c
@@ -274,27 +274,25 @@ static int updatesymlinks_addlink (stralloc5 *blah, unsigned int dstpos, unsigne
   return MODIFIED ;
 }
 
-int main (int argc, char *const *argv)
+int main (int argc, char const *const *argv)
 {
-  stralloc5 blah = STRALLOC5_ZERO ;
   PROG = "s6-update-symlinks" ;
   if (argc < 3) strerr_dieusage(100, USAGE) ;
   {
-    char *const *p = argv + 1 ;
-    for (; *p ; p++) if (**p != '/') strerr_dieusage(100, USAGE) ;
-  }
-  {
+    stralloc5 blah = STRALLOC5_ZERO ;
+    char const *const *p = argv + 1 ;
     size_t i = strlen(argv[1]) ;
-    while (i && (argv[1][i-1] == '/')) argv[1][--i] = 0 ;
+    char target[i+1] ;
+    for (; *p ; p++) if (**p != '/') strerr_dieusage(100, USAGE) ;
+    p = argv + 2 ;
+    memcpy(target, argv[1], i+1) ;
+    while (i && (target[i-1] == '/')) target[--i] = 0 ;
     if (!i) strerr_diefu1x(100, "replace root directory") ;
-  }
-  if (!updatesymlinks_makeuniquename(&blah.dst, argv[1], UPDATESYMLINKS_MAGICNEW))
-    strerr_diefu2sys(111, "make random unique name based on ", argv[1]) ;
-  if ((unlink(blah.dst.s) == -1) && (errno != ENOENT))
-    strerr_diefu2sys(111, "unlink ", blah.dst.s) ;
+    if (!updatesymlinks_makeuniquename(&blah.dst, target, UPDATESYMLINKS_MAGICNEW))
+      strerr_diefu2sys(111, "make random unique name based on ", target) ;
+    if ((unlink(blah.dst.s) == -1) && (errno != ENOENT))
+      strerr_diefu2sys(111, "unlink ", blah.dst.s) ;
 
-  {
-    char *const *p = argv + 2 ;
     for (; *p ; p++)
     {
       int r ;
@@ -314,35 +312,34 @@ int main (int argc, char *const *argv)
           strerr_dief2sys(111, "error processing ", *p) ;
       }
     }
-  }
-  stralloc_free(&blah.tmp) ;
+    stralloc_free(&blah.tmp) ;
 
-  if (rename(blah.dst.s, argv[1]) == -1) /* be atomic if possible */
-  {
-    blah.src.len = 0 ;
-    if (!updatesymlinks_makeuniquename(&blah.src, argv[1], UPDATESYMLINKS_MAGICOLD))
+    if (rename(blah.dst.s, target) == -1) /* be atomic if possible */
     {
-      updatesymlinks_cleanup(&blah.dst, 0) ;
-      strerr_diefu2sys(111, "make random unique name based on ", argv[1]) ;
-    }
+      blah.src.len = 0 ;
+      if (!updatesymlinks_makeuniquename(&blah.src, target, UPDATESYMLINKS_MAGICOLD))
+      {
+        updatesymlinks_cleanup(&blah.dst, 0) ;
+        strerr_diefu2sys(111, "make random unique name based on ", target) ;
+      }
 
-    if (rename(argv[1], blah.src.s) == -1)
-    {
-      updatesymlinks_cleanup(&blah.dst, 0) ;
-      strerr_diefu4sys(111, "rename ", argv[1], " to ", blah.src.s) ;
-    }
-   /* XXX: unavoidable race condition here: argv[1] does not exist */
-    if (rename(blah.dst.s, argv[1]) == -1)
-    {
-      rename(blah.src.s, argv[1]) ;
-      updatesymlinks_cleanup(&blah.dst, 0) ;
-      strerr_diefu4sys(111, "rename ", blah.dst.s, " to ", argv[1]) ;
+      if (rename(target, blah.src.s) == -1)
+      {
+        updatesymlinks_cleanup(&blah.dst, 0) ;
+        strerr_diefu4sys(111, "rename ", target, " to ", blah.src.s) ;
+      }
+     /* XXX: unavoidable race condition here: target does not exist */
+      if (rename(blah.dst.s, target) == -1)
+      {
+        rename(blah.src.s, target) ;
+        updatesymlinks_cleanup(&blah.dst, 0) ;
+        strerr_diefu4sys(111, "rename ", blah.dst.s, " to ", target) ;
+      }
+      stralloc_free(&blah.dst) ;
+      if (rm_rf_in_tmp(&blah.src, 0) == -1)
+        strerr_warnwu2sys("remove old directory ", blah.src.s) ;
+      stralloc_free(&blah.src) ;
     }
-    stralloc_free(&blah.dst) ;
-    if (rm_rf_in_tmp(&blah.src, 0) == -1)
-      strerr_warnwu2sys("remove old directory ", blah.src.s) ;
-    stralloc_free(&blah.src) ;
   }
-
   return 0 ;
 }
diff --git a/src/skaembutils/seekablepipe.c b/src/s6-portable-utils/seekablepipe.c
index 5ae6a2d..5ae6a2d 100644
--- a/src/skaembutils/seekablepipe.c
+++ b/src/s6-portable-utils/seekablepipe.c
diff --git a/src/skaembutils/deps-exe/s6-ln b/src/skaembutils/deps-exe/s6-ln
deleted file mode 100644
index 720fe7d..0000000
--- a/src/skaembutils/deps-exe/s6-ln
+++ /dev/null
@@ -1,3 +0,0 @@
--lskarnet
-${SOCKET_LIB}
-${SYSCLOCK_LIB}
diff --git a/src/skaembutils/deps-exe/s6-uniquename b/src/skaembutils/deps-exe/s6-uniquename
deleted file mode 100644
index 720fe7d..0000000
--- a/src/skaembutils/deps-exe/s6-uniquename
+++ /dev/null
@@ -1,3 +0,0 @@
--lskarnet
-${SOCKET_LIB}
-${SYSCLOCK_LIB}
diff --git a/src/skaembutils/deps-exe/s6-update-symlinks b/src/skaembutils/deps-exe/s6-update-symlinks
deleted file mode 100644
index 720fe7d..0000000
--- a/src/skaembutils/deps-exe/s6-update-symlinks
+++ /dev/null
@@ -1,3 +0,0 @@
--lskarnet
-${SOCKET_LIB}
-${SYSCLOCK_LIB}
diff --git a/tools/gen-multicall.sh b/tools/gen-multicall.sh
new file mode 100755
index 0000000..50a9625
--- /dev/null
+++ b/tools/gen-multicall.sh
@@ -0,0 +1,84 @@
+#!/bin/sh -e
+
+P="$1"
+p=`echo $P | tr - _`
+
+echo '/* ISC license. */'
+echo
+echo '#include <skalibs/nonposix.h>'
+echo
+{ echo '#include <string.h>' ; echo '#include <stdlib.h>' ; cat src/$P/*.c | grep '^#include <' | grep -vF '<skalibs/' | grep -vF "<$P/" ; } | sort -u
+
+cat <<EOF
+
+#include <skalibs/skalibs.h>
+
+#include <$P/config.h>
+
+typedef int main_func (int, char const *const *, char const *const *) ;
+typedef main_func *main_func_ref ;
+
+typedef struct multicall_app_s multicall_app, *multicall_app_ref ;
+struct multicall_app_s
+{
+  char const *name ;
+  main_func_ref mainf ;
+} ;
+
+static int multicall_app_cmp (void const *a, void const *b)
+{
+  char const *name = a ;
+  multicall_app const *p = b ;
+  return strcmp(name, p->name) ;
+}
+EOF
+
+for i in `ls -1 src/$P/deps-exe` ; do
+  j=`echo $i | tr - _`
+  echo
+  grep -v '^#include ' < src/$P/${i}.c | grep -vF '/* ISC license. */' | sed -e "s/int main (.*)$/int ${j}_main (int argc, char const *const *argv, char const *const *envp)/"
+  echo
+  echo '#undef USAGE'
+  echo '#undef dieusage'
+  echo '#undef dienomem'
+  echo '#undef bail'
+done
+
+cat <<EOF
+
+static int ${p}_main (int, char const *const *, char const *const *) ;
+
+static multicall_app const multicall_apps[] =
+{
+EOF
+
+for i in `{ echo $P ; ls -1 src/$P/deps-exe ; } | sort` ; do
+  j=`echo $i | tr - _`
+  echo "  { .name = \"${i}\", .mainf = &${j}_main },"
+done
+
+cat <<EOF
+} ;
+
+#define USAGE "$P subcommand [ arguments... ]"
+#define dieusage() strerr_dief1x(100, USAGE)
+
+static int ${p}_main (int argc, char const *const *argv, char const *const *envp)
+{
+  multicall_app const *p ;
+  PROG = "$P" ;
+  if (!argc) dieusage() ;
+  p = bsearch(argv[1], multicall_apps, sizeof(multicall_apps) / sizeof(multicall_app), sizeof(multicall_app), &multicall_app_cmp) ;
+  if (!p) strerr_dief2x(100, "unknown subcommand: ", argv[1]) ;
+  return (*(p->mainf))(argc-1, argv+1, envp) ;
+}
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+  multicall_app const *p ;
+  char const *name = strrchr(argv[0], '/') ;
+  if (name) name++ ; else name = argv[0] ;
+  p = bsearch(name, multicall_apps, sizeof(multicall_apps) / sizeof(multicall_app), sizeof(multicall_app), &multicall_app_cmp) ;
+  return p ? (*(p->mainf))(argc, argv, envp) : ${p}_main(argc, argv, envp) ;
+}
+EOF
diff --git a/tools/install.sh b/tools/install.sh
index 89f9428..e96dd7b 100755
--- a/tools/install.sh
+++ b/tools/install.sh
@@ -1,19 +1,21 @@
 #!/bin/sh
 
 usage() {
-  echo "usage: $0 [-D] [-l] [-m mode] src dst" 1>&2
+  echo "usage: $0 [ -D ] [ -l ] [ -m mode ] [ -O owner:group ] src dst" 1>&2
   exit 1
 }
 
 mkdirp=false
 symlink=false
 mode=0755
+og=
 
-while getopts Dlm: name ; do
+while getopts Dlm:O: name ; do
   case "$name" in
     D) mkdirp=true ;;
     l) symlink=true ;;
     m) mode=$OPTARG ;;
+    O) og=$OPTARG ;;
     ?) usage ;;
   esac
 done
@@ -46,7 +48,10 @@ if $symlink ; then
   ln -s "$src" "$tmp"
 else
   cat < "$1" > "$tmp"
-  chmod "$mode" "$tmp"
+  if test -n "$og" ; then
+    chown -- "$og" "$tmp"
+  fi
+  chmod -- "$mode" "$tmp"
 fi
 
 mv -f "$tmp" "$dst"