summary refs log tree commit diff
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2023-02-01 18:08:26 +0000
committerLaurent Bercot <ska@appnovation.com>2023-02-01 18:08:26 +0000
commitedf81b0d16322e5d49ec22f394b669d9094daac1 (patch)
tree04b1a40a5af1c8d95978e0e85d96cb8e5cb061bb
parent5b5f5e507db58c5005cd8a572f761ef2f002cb0f (diff)
downloadexecline-edf81b0d16322e5d49ec22f394b669d9094daac1.tar.gz
execline-edf81b0d16322e5d49ec22f394b669d9094daac1.tar.xz
execline-edf81b0d16322e5d49ec22f394b669d9094daac1.zip
Add a multicall target. If you use it, you're on your own.
Signed-off-by: Laurent Bercot <ska@appnovation.com>
-rw-r--r--.gitignore3
-rw-r--r--Makefile6
-rw-r--r--package/deps.mak2
-rw-r--r--package/targets.mak10
-rw-r--r--src/execline/case.c6
-rw-r--r--src/execline/eltest.c14
-rw-r--r--src/execline/forstdin.c26
-rw-r--r--src/execline/forx.c6
-rw-r--r--src/execline/loopwhilex.c4
-rw-r--r--src/execline/trap.c36
-rw-r--r--src/multicall/deps-exe/execline5
-rwxr-xr-xtools/gen-multicall.sh97
12 files changed, 167 insertions, 48 deletions
diff --git a/.gitignore b/.gitignore
index acbc423..1e09951 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,6 +12,7 @@
 /elgetopt
 /elgetpositionals
 /elglob
+/eltest
 /emptyenv
 /envfile
 /exec
@@ -55,3 +56,5 @@
 /unexport
 /wait
 /withstdinas
+/src/multicall/execline.c
+/execline
diff --git a/Makefile b/Makefile
index d961d7f..483403f 100644
--- a/Makefile
+++ b/Makefile
@@ -19,6 +19,8 @@ SHARED_LIBS :=
 INTERNAL_LIBS :=
 EXTRA_TARGETS :=
 LIB_DEFS :=
+EXTRA_BINS :=
+EXTRA_TEMP :=
 
 define library_definition
 LIB$(firstword $(subst =, ,$(1))) := lib$(lastword $(subst =, ,$(1))).$(if $(DO_ALLSTATIC),a,so).xyzzy
@@ -60,7 +62,7 @@ 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)
+	@exec rm -f $(ALL_LIBS) $(ALL_BINS) $(EXTRA_BINS) $(EXTRA_TEMP) $(wildcard src/*/*.o src/*/*.lo)
 
 distclean: clean
 	@exec rm -f config.mak src/include/$(package)/config.h
@@ -134,7 +136,7 @@ $(DESTDIR)$(includedir)/$(package)/%.h: src/include/$(package)/%.h
 %.lo: %.c
 	exec $(CC) $(CPPFLAGS_ALL) $(CFLAGS_ALL) $(CFLAGS_SHARED) -c -o $@ $<
 
-$(ALL_BINS):
+$(ALL_BINS) $(EXTRA_BINS):
 	exec $(CC) -o $@ $(CFLAGS_ALL) $(LDFLAGS_ALL) $(LDFLAGS_NOSHARED) $^ $(EXTRA_LIBS) $(LDLIBS)
 
 lib%.a.xyzzy:
diff --git a/package/deps.mak b/package/deps.mak
index d9ab00a..fa81ab3 100644
--- a/package/deps.mak
+++ b/package/deps.mak
@@ -181,6 +181,8 @@ libexecline.a.xyzzy: src/libexecline/el_execsequence.lo src/libexecline/el_getst
 endif
 libexecline.so.xyzzy: EXTRA_LIBS := -lskarnet
 libexecline.so.xyzzy: src/libexecline/el_execsequence.lo src/libexecline/el_getstrict.lo src/libexecline/el_modif_and_exec.lo src/libexecline/el_modif_and_spawn.lo src/libexecline/el_parse.lo src/libexecline/el_parse_from_buffer.lo src/libexecline/el_parse_from_string.lo src/libexecline/el_popenv.lo src/libexecline/el_pushenv.lo src/libexecline/el_semicolon.lo src/libexecline/el_spawn0.lo src/libexecline/el_spawn1.lo src/libexecline/el_substandrun.lo src/libexecline/el_substandrun_str.lo src/libexecline/el_substitute.lo src/libexecline/el_transform.lo src/libexecline/el_vardupl.lo src/libexecline/exlsn_define.lo src/libexecline/exlsn_elglob.lo src/libexecline/exlsn_importas.lo src/libexecline/exlsn_multidefine.lo src/libexecline/exlsn_exlp.lo src/libexecline/exlsn_main.lo src/libexecline/exlsn_free.lo src/libexecline/exlp.lo
+execline: EXTRA_LIBS := -lskarnet ${SPAWN_LIB} ${MAYBEPTHREAD_LIB}
+execline: src/multicall/execline.o ${LIBEXECLINE} ${LIBNSSS}
 posix-cd: EXTRA_LIBS := -lskarnet
 posix-cd: src/posix/posix-cd.o
 posix-umask: EXTRA_LIBS := -lskarnet
diff --git a/package/targets.mak b/package/targets.mak
index 0b41b79..258ed41 100644
--- a/package/targets.mak
+++ b/package/targets.mak
@@ -67,3 +67,13 @@ $(DESTDIR)$(bindir)/cd: $(DESTDIR)$(bindir)/$(PEDANTIC_PREFIX)-cd
 
 $(DESTDIR)$(bindir)/umask: $(DESTDIR)$(bindir)/$(PEDANTIC_PREFIX)-umask
 	exec ./tools/install.sh -l $(PEDANTIC_PREFIX)-umask $(DESTDIR)$(bindir)/umask
+
+
+EXTRA_BINS := execline
+EXTRA_TEMP := src/multicall/execline.c
+
+multicall: execline
+.PHONY: multicall
+
+src/multicall/execline.c: tools/gen-multicall.sh src/execline/deps-exe src/include/execline/config.h src/include/execline/execline.h
+	./tools/gen-multicall.sh > src/multicall/execline.c
diff --git a/src/execline/case.c b/src/execline/case.c
index ae5b5db..b62583c 100644
--- a/src/execline/case.c
+++ b/src/execline/case.c
@@ -15,8 +15,8 @@
 #define USAGE "case [ -s | -S ] [ -e | -E ] [ -n | -N ] [ -i ] value { re1 { prog1... } re2 { prog2... } ... } progdefault... "
 #define dieusage() strerr_dieusage(100, USAGE)
 
-static void execit (char const *const *argv, char const *expr, char const *s, regmatch_t const *pmatch, size_t n) gccattr_noreturn ;
-static void execit (char const *const *argv, char const *expr, char const *s, regmatch_t const *pmatch, size_t n)
+static void case_execit (char const *const *argv, char const *expr, char const *s, regmatch_t const *pmatch, size_t n) gccattr_noreturn ;
+static void case_execit (char const *const *argv, char const *expr, char const *s, regmatch_t const *pmatch, size_t n)
 {
   if (n)
   {
@@ -125,7 +125,7 @@ int main (int argc, char const **argv, char const *const *envp)
         if (!r)
         {
           argv[i + argc2] = 0 ;
-          execit(argv + i, expr, s, pmatch, flagnosub ? 0 : 1 + re.re_nsub) ;
+          case_execit(argv + i, expr, s, pmatch, flagnosub ? 0 : 1 + re.re_nsub) ;
         }
         if (r != REG_NOMATCH)
         {
diff --git a/src/execline/eltest.c b/src/execline/eltest.c
index b14318b..93ff0b4 100644
--- a/src/execline/eltest.c
+++ b/src/execline/eltest.c
@@ -74,7 +74,7 @@ struct node
   char const *data ;
 } ;
 
-static unsigned int lex (struct node *tree, char const *const *argv)
+static unsigned int eltest_lex (struct node *tree, char const *const *argv)
 {
   static struct token const tokens[46] =
   {
@@ -149,7 +149,7 @@ static unsigned int lex (struct node *tree, char const *const *argv)
   return pos ;
 }
 
-static unsigned int parse (struct node *tree, unsigned int n)
+static unsigned int eltest_parse (struct node *tree, unsigned int n)
 {
   static char const table[9][13] =
   {
@@ -278,16 +278,16 @@ static unsigned int parse (struct node *tree, unsigned int n)
   return stack[1] ;
 }
 
-static int run (struct node const *tree, unsigned int root)
+static int eltest_run (struct node const *tree, unsigned int root)
 {
   switch (tree[root].op)
   {
     case T_NOT :
-      return !run(tree, tree[root].arg1) ;
+      return !eltest_run(tree, tree[root].arg1) ;
     case T_AND :
-      return run(tree, tree[root].arg1) && run(tree, tree[root].arg2) ;
+      return eltest_run(tree, tree[root].arg1) && eltest_run(tree, tree[root].arg2) ;
     case T_OR :
-      return run(tree, tree[root].arg1) || run(tree, tree[root].arg2) ;
+      return eltest_run(tree, tree[root].arg1) || eltest_run(tree, tree[root].arg2) ;
     case T_EXIST :
       return access(tree[tree[root].arg1].data, F_OK) == 0 ;
     case T_BLOCK :
@@ -501,5 +501,5 @@ int main (int argc, char const *const *argv)
   struct node tree[argc+2] ;
   if (argc <= 1) return 1 ;
   PROG = "eltest" ;
-  return !run(tree, parse(tree, lex(tree, argv+1))) ;
+  return !eltest_run(tree, eltest_parse(tree, eltest_lex(tree, argv+1))) ;
 }
diff --git a/src/execline/forstdin.c b/src/execline/forstdin.c
index 1714be3..e365866 100644
--- a/src/execline/forstdin.c
+++ b/src/execline/forstdin.c
@@ -19,9 +19,9 @@
 #define USAGE "forstdin [ -E | -e ] [ -p | -o okcode,okcode,... | -x breakcode,breakcode,... ] [ -N | -n ] [ -C | -c ] [ -0 | -d delim ] var command..."
 #define dieusage() strerr_dieusage(100, USAGE)
 
-static genalloc pids = GENALLOC_ZERO ; /* pid_t */
+static genalloc forstdin_pids = GENALLOC_ZERO ; /* pid_t */
 
-static int isok (unsigned short *tab, unsigned int n, int code)
+static int fs_isok (unsigned short *tab, unsigned int n, int code)
 {
   unsigned int i = 0 ;
   for (; i < n ; i++) if ((unsigned short)code == tab[i]) break ;
@@ -30,8 +30,8 @@ static int isok (unsigned short *tab, unsigned int n, int code)
 
 static void parallel_sigchld_handler (int sig)
 {
-  pid_t *tab = genalloc_s(pid_t, &pids) ;
-  size_t len = genalloc_len(pid_t, &pids) ;
+  pid_t *tab = genalloc_s(pid_t, &forstdin_pids) ;
+  size_t len = genalloc_len(pid_t, &forstdin_pids) ;
   int wstat ;
   for (;;)
   {
@@ -39,7 +39,7 @@ static void parallel_sigchld_handler (int sig)
     if (r <= 0) break ;
     tab[r-1] = tab[--len] ;
   }
-  genalloc_setlen(pid_t, &pids, len) ;
+  genalloc_setlen(pid_t, &forstdin_pids, len) ;
   (void)sig ;
 }
 
@@ -62,7 +62,7 @@ int main (int argc, char const **argv)
       {
         case 'p' :
         {
-          if (!genalloc_ready(pid_t, &pids, 1))
+          if (!genalloc_ready(pid_t, &forstdin_pids, 1))
             strerr_diefu1sys(111, "genalloc_ready") ;
           break ;
         }
@@ -90,7 +90,7 @@ int main (int argc, char const **argv)
   if (argc < 2) dieusage() ;
   if (!argv[0][0] || strchr(argv[0], '=')) strerr_dief1x(100, "invalid variable name") ;
 
-  if (pids.s)
+  if (forstdin_pids.s)
   {
     if (!sig_catch(SIGCHLD, &parallel_sigchld_handler))
       strerr_diefu1sys(111, "install SIGCHLD handler") ;
@@ -122,12 +122,12 @@ int main (int argc, char const **argv)
     }
     eofcode = 0 ;
     if (!stralloc_0(&value)) strerr_diefu1sys(111, "stralloc_0") ;
-    if (pids.s) sig_block(SIGCHLD) ;
+    if (forstdin_pids.s) sig_block(SIGCHLD) ;
     pid = el_modif_and_spawn(argv + 1, argv[0], value.s, doimport) ;
     if (!pid) strerr_diefu2sys(111, "spawn ", argv[1]) ;
-    if (pids.s)
+    if (forstdin_pids.s)
     {
-      if (!genalloc_append(pid_t, &pids, &pid))
+      if (!genalloc_append(pid_t, &forstdin_pids, &pid))
         strerr_diefu1sys(111, "genalloc_append") ;
       sig_unblock(SIGCHLD) ;
     }
@@ -136,18 +136,18 @@ int main (int argc, char const **argv)
       int wstat ;
       if (wait_pid(pid, &wstat) < 0)
         strerr_diefu2sys(111, "wait for ", argv[1]) ;
-      if (not == isok(okcodes, nbc, wait_estatus(wstat)))
+      if (not == fs_isok(okcodes, nbc, wait_estatus(wstat)))
         return wait_estatus(wstat) ;
     }
   }
-  if (pids.s)
+  if (forstdin_pids.s)
   {
     sigset_t empty ;
     sigemptyset(&empty) ;
     sig_block(SIGCHLD) ;
     for (;;)
     {
-      if (!pids.len) break ;
+      if (!forstdin_pids.len) break ;
       sigsuspend(&empty) ;
     }
   }
diff --git a/src/execline/forx.c b/src/execline/forx.c
index fa6c0e3..1695c00 100644
--- a/src/execline/forx.c
+++ b/src/execline/forx.c
@@ -16,7 +16,7 @@
 #define USAGE "forx [ -E | -e ] [ -p ] [ -o okcode,okcode,... | -x breakcode,breakcode,... ] var { values... } command..."
 #define dieusage() strerr_dieusage(100, USAGE)
 
-static int isok (unsigned short const *tab, unsigned int n, int code)
+static int fx_isok (unsigned short const *tab, unsigned int n, int code)
 {
   unsigned int i = 0 ;
   for (; i < n ; i++) if ((unsigned short)code == tab[i]) break ;
@@ -35,7 +35,7 @@ static int waitn_code (unsigned short const *tab, unsigned int nbc, pid_t *pids,
     for (; i < n ; i++) if (pid == pids[i]) break ;
     if (i < n)
     {
-      if (not == isok(tab, nbc, wait_estatus(wstat))) ok = 0 ;
+      if (not == fx_isok(tab, nbc, wait_estatus(wstat))) ok = 0 ;
       pids[i] = pids[--n] ;
     }
   }
@@ -94,7 +94,7 @@ int main (int argc, char const **argv)
         int wstat ;
         if (wait_pid(pid, &wstat) == -1)
           strerr_diefu2sys(111, "wait for ", argv[argc1+1]) ;
-        if (not == isok(okcodes, nbc, wait_estatus(wstat)))
+        if (not == fx_isok(okcodes, nbc, wait_estatus(wstat)))
           return wait_estatus(wstat) ;
       }
     }
diff --git a/src/execline/loopwhilex.c b/src/execline/loopwhilex.c
index b29d26a..acae3b3 100644
--- a/src/execline/loopwhilex.c
+++ b/src/execline/loopwhilex.c
@@ -12,7 +12,7 @@
 #define USAGE "loopwhilex [ -n ] [ -o okcode,okcode,... | -x exitcode,exitcode,... ] prog..."
 #define dieusage() strerr_dieusage(100, USAGE)
 
-static int isok (unsigned short *tab, unsigned int n, int code)
+static int lw_isok (unsigned short *tab, unsigned int n, int code)
 {
   unsigned int i = 0 ;
   for (; i < n ; i++) if ((unsigned short)code == tab[i]) break ;
@@ -67,7 +67,7 @@ int main (int argc, char const *const *argv, char const *const *envp)
       else strerr_diefu2sys(111, "spawn ", argv[0]) ;
     }
     if (wait_pid(pid, &wstat) < 0) strerr_diefu1sys(111, "wait_pid") ;
-    cont = not != isok(okcodes, nbc, wait_estatus(wstat)) ;
+    cont = not != lw_isok(okcodes, nbc, wait_estatus(wstat)) ;
   }
   return wait_estatus(wstat) ;
 }
diff --git a/src/execline/trap.c b/src/execline/trap.c
index cc13595..630f865 100644
--- a/src/execline/trap.c
+++ b/src/execline/trap.c
@@ -20,30 +20,30 @@
 #define USAGE "trap [ -x ] { signal { cmdline } ... } prog..."
 #define dieusage() strerr_dieusage(100, USAGE) ;
 
-static pid_t pids[SKALIBS_NSIG + 1] ;
-static char const *const *argvs[SKALIBS_NSIG] ; /* initted with 0s */
+static pid_t trap_pids[SKALIBS_NSIG + 1] ;
+static char const *const *trap_argvs[SKALIBS_NSIG] ; /* initted with 0s */
 
 static inline void action (unsigned int i, char const *const *envp, size_t envlen)
 {
-  if (argvs[i])
+  if (trap_argvs[i])
   {
-    if (!pids[i])
+    if (!trap_pids[i])
     {
       char const *newenvp[envlen + 3] ;
       char modif[9 + PID_FMT + UINT_FMT] = "!=" ;
       size_t m = 2 ;
-      m += pid_fmt(modif + m, pids[SKALIBS_NSIG]) ;
+      m += pid_fmt(modif + m, trap_pids[SKALIBS_NSIG]) ;
       modif[m++] = 0 ;
       memcpy(modif + m, "SIGNAL=", 7) ; m += 7 ;
       m += uint_fmt(modif + m, i) ;
       modif[m++] = 0 ;
       if (!env_mergen(newenvp, envlen + 3, envp, envlen, modif, m, 2))
         strerr_diefu1sys(111, "adjust environment for child") ;
-      pids[i] = child_spawn0(argvs[i][0], argvs[i], newenvp) ;
-      if (!pids[i]) strerr_diefu2sys(111, "spawn ", argvs[i][0]) ;
+      trap_pids[i] = child_spawn0(trap_argvs[i][0], trap_argvs[i], newenvp) ;
+      if (!trap_pids[i]) strerr_diefu2sys(111, "spawn ", trap_argvs[i][0]) ;
     }
   }
-  else kill(pids[SKALIBS_NSIG], i) ;
+  else kill(trap_pids[SKALIBS_NSIG], i) ;
 }
 
 int main (int argc, char const **argv, char const *const *envp)
@@ -89,10 +89,10 @@ int main (int argc, char const **argv, char const *const *envp)
     argc2 = el_semicolon(argv + ++i) ;
     if (i + argc2 >= argc1)
       strerr_dief3x(100, "unterminated", " internal block for directive ", argv[i-1]) ;
-    if (argvs[sig])
+    if (trap_argvs[sig])
       strerr_dief3x(100, "duplicate", " directive: ", argv[i-1]) ;
     if (!sig || (sig != SIGCHLD && sigismember(&full, sig) > 0))
-      argvs[sig] = argv + i ;
+      trap_argvs[sig] = argv + i ;
     else
     {
       char fmt[UINT_FMT] ;
@@ -103,10 +103,10 @@ int main (int argc, char const **argv, char const *const *envp)
     i += argc2 + 1 ;
   }
 
-  if (argvs[0])
+  if (trap_argvs[0])
     for (i = 1 ; i < SKALIBS_NSIG ; i++)
-      if (!argvs[i] && sigismember(&full, i) > 0)
-        argvs[i] = argvs[0] ;
+      if (!trap_argvs[i] && sigismember(&full, i) > 0)
+        trap_argvs[i] = trap_argvs[0] ;
 
   if (xfersigs) set = full ;
   else
@@ -114,7 +114,7 @@ int main (int argc, char const **argv, char const *const *envp)
     sigemptyset(&set) ;
     sigaddset(&set, SIGCHLD) ;
     for (i = 1 ; i < SKALIBS_NSIG ; i++)
-      if (argvs[i])
+      if (trap_argvs[i])
         sigaddset(&set, i) ;
   }
 
@@ -122,8 +122,8 @@ int main (int argc, char const **argv, char const *const *envp)
   if (x.fd < 0) strerr_diefu1sys(111, "selfpipe_init") ;
   if (!selfpipe_trapset(&set)) strerr_diefu1sys(111, "trap signals") ;
 
-  pids[SKALIBS_NSIG] = child_spawn0(argv[argc1 + 1], argv + argc1 + 1, envp) ;
-  if (!pids[SKALIBS_NSIG]) strerr_diefu2sys(111, "spawn ", argv[argc1 + 1]) ;
+  trap_pids[SKALIBS_NSIG] = child_spawn0(argv[argc1 + 1], argv + argc1 + 1, envp) ;
+  if (!trap_pids[SKALIBS_NSIG]) strerr_diefu2sys(111, "spawn ", argv[argc1 + 1]) ;
 
  loop:
   if (iopause_g(&x, 1, 0) < 0) strerr_diefu1sys(111, "iopause") ;
@@ -138,11 +138,11 @@ int main (int argc, char const **argv, char const *const *envp)
         for (;;)
         {
           int wstat ;
-          ssize_t id = wait_pids_nohang(pids, SKALIBS_NSIG + 1, &wstat) ;
+          ssize_t id = wait_pids_nohang(trap_pids, SKALIBS_NSIG + 1, &wstat) ;
           if (id < 0 && errno != ECHILD)
             strerr_diefu1sys(111, "wait") ;
           if (id <= 0) break ;
-          pids[id - 1] = 0 ;
+          trap_pids[id - 1] = 0 ;
           if (id == SKALIBS_NSIG + 1) return wait_estatus(wstat) ;
         }
         break ;
diff --git a/src/multicall/deps-exe/execline b/src/multicall/deps-exe/execline
new file mode 100644
index 0000000..d024a89
--- /dev/null
+++ b/src/multicall/deps-exe/execline
@@ -0,0 +1,5 @@
+${LIBEXECLINE}
+${LIBNSSS}
+-lskarnet
+${SPAWN_LIB}
+${MAYBEPTHREAD_LIB}
diff --git a/tools/gen-multicall.sh b/tools/gen-multicall.sh
new file mode 100755
index 0000000..d9b03d3
--- /dev/null
+++ b/tools/gen-multicall.sh
@@ -0,0 +1,97 @@
+#!/bin/sh -e
+
+echo '/* ISC license. */'
+echo
+echo '#include <skalibs/nonposix.h>'
+echo
+{ echo '#include <string.h>' ; echo '#include <stdlib.h>' ; cat src/execline/*.c | grep '^#include <' | grep -vF '<skalibs/' | grep -vF '<execline/' ; } | sort -u
+
+cat <<EOF
+
+#include <skalibs/nsig.h>
+#include <skalibs/skalibs.h>
+
+#include <execline/config.h>
+#include <execline/execline.h>
+#include "exlsn.h"
+
+typedef int main_func (int, char **, char const *const *) ;
+typedef main_func *main_func_ref ;
+
+typedef struct execline_app_s execline_app, *execline_app_ref ;
+struct execline_app_s
+{
+  char const *name ;
+  main_func_ref mainf ;
+} ;
+
+static int execline_app_cmp (void const *a, void const *b)
+{
+  char const *name = a ;
+  execline_app const *p = b ;
+  return strcmp(name, p->name) ;
+}
+
+#ifdef EXECLINE_PEDANTIC_POSIX
+# define CD_FUNC posix_cd_main
+# define UMASK_FUNC posix_umask_main
+#else
+# define CD_FUNC execline_cd_main
+# define UMASK_FUNC execline_umask_main
+#endif
+
+EOF
+
+for i in `ls -1 src/execline/deps-exe` ; do
+  j=`echo $i | tr - _`
+  echo
+  grep -v '^#include ' < src/execline/${i}.c | grep -vF '/* ISC license. */' | sed -e "s/int main (int argc, char \(.*\)\*argv.*$/int ${j}_main (int argc, char \1*argv, char const *const *envp)/"
+  echo
+  echo '#undef USAGE'
+  echo '#undef dieusage'
+done
+
+cat <<EOF
+
+static int execline_main (int, char **, char const *const *) ;
+
+static execline_app const execline_apps[] =
+{
+EOF
+
+for i in `{ echo cd ; echo execline ; echo umask ; ls -1 src/execline/deps-exe ; } | sort` ; do
+  j=`echo $i | tr - _`
+  if test $i = cd ; then
+    echo '  { .name = "cd", .mainf = (main_func_ref)&CD_FUNC },'
+  elif test $i = umask ; then
+    echo '  { .name = "umask", .mainf = (main_func_ref)&UMASK_FUNC },'
+  else
+    echo "  { .name=\"${i}\", .mainf = (main_func_ref)&${j}_main },"
+  fi
+done
+
+cat <<EOF
+} ;
+
+#define USAGE "execline subcommand [ arguments... ]"
+#define dieusage() strerr_dief1x(100, USAGE)
+
+static int execline_main (int argc, char **argv, char const *const *envp)
+{
+  execline_app const *p ;
+  PROG = "execline" ;
+  if (!argc) dieusage() ;
+  p = bsearch(argv[1], execline_apps, sizeof(execline_apps) / sizeof(execline_app), sizeof(execline_app), &execline_app_cmp) ;
+  if (!p) strerr_dief2x(100, "unknown subcommand: ", argv[1]) ;
+  return (*(p->mainf))(argc-1, argv+1, envp) ;
+}
+
+int main (int argc, char **argv, char const *const *envp)
+{
+  execline_app const *p ;
+  char const *name = strrchr(argv[0], '/') ;
+  if (name) name++ ; else name = argv[0] ;
+  p = bsearch(name, execline_apps, sizeof(execline_apps) / sizeof(execline_app), sizeof(execline_app), &execline_app_cmp) ;
+  return p ? (*(p->mainf))(argc, argv, envp) : execline_main(argc, argv, envp) ;
+}
+EOF