summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/execline/background.c76
-rw-r--r--src/execline/backtick.c84
-rw-r--r--src/execline/cd.c17
-rw-r--r--src/execline/define.c12
-rw-r--r--src/execline/deps-exe/background2
-rw-r--r--src/execline/deps-exe/backtick2
-rw-r--r--src/execline/deps-exe/cd1
-rw-r--r--src/execline/deps-exe/define2
-rw-r--r--src/execline/deps-exe/dollarat1
-rw-r--r--src/execline/deps-exe/elgetopt2
-rw-r--r--src/execline/deps-exe/elgetpositionals2
-rw-r--r--src/execline/deps-exe/elglob2
-rw-r--r--src/execline/deps-exe/emptyenv2
-rw-r--r--src/execline/deps-exe/exec1
-rw-r--r--src/execline/deps-exe/execlineb2
-rw-r--r--src/execline/deps-exe/exit1
-rw-r--r--src/execline/deps-exe/export1
-rw-r--r--src/execline/deps-exe/fdblock1
-rw-r--r--src/execline/deps-exe/fdclose1
-rw-r--r--src/execline/deps-exe/fdmove1
-rw-r--r--src/execline/deps-exe/fdreserve1
-rw-r--r--src/execline/deps-exe/forbacktickx2
-rw-r--r--src/execline/deps-exe/foreground2
-rw-r--r--src/execline/deps-exe/forx2
-rw-r--r--src/execline/deps-exe/getpid1
-rw-r--r--src/execline/deps-exe/heredoc1
-rw-r--r--src/execline/deps-exe/homeof1
-rw-r--r--src/execline/deps-exe/if2
-rw-r--r--src/execline/deps-exe/ifelse2
-rw-r--r--src/execline/deps-exe/ifte2
-rw-r--r--src/execline/deps-exe/ifthenelse2
-rw-r--r--src/execline/deps-exe/import2
-rw-r--r--src/execline/deps-exe/importas2
-rw-r--r--src/execline/deps-exe/loopwhilex2
-rw-r--r--src/execline/deps-exe/multidefine2
-rw-r--r--src/execline/deps-exe/multisubstitute2
-rw-r--r--src/execline/deps-exe/pipeline2
-rw-r--r--src/execline/deps-exe/piperw1
-rw-r--r--src/execline/deps-exe/redirfd1
-rw-r--r--src/execline/deps-exe/runblock2
-rw-r--r--src/execline/deps-exe/shift2
-rw-r--r--src/execline/deps-exe/tryexec2
-rw-r--r--src/execline/deps-exe/umask1
-rw-r--r--src/execline/deps-exe/unexport1
-rw-r--r--src/execline/deps-exe/wait2
-rw-r--r--src/execline/dollarat.c63
-rw-r--r--src/execline/elgetopt.c69
-rw-r--r--src/execline/elgetpositionals.c12
-rw-r--r--src/execline/elglob.c12
-rw-r--r--src/execline/emptyenv.c92
-rw-r--r--src/execline/exec.c48
-rw-r--r--src/execline/execlineb.c327
-rw-r--r--src/execline/exit.c15
-rw-r--r--src/execline/export.c27
-rw-r--r--src/execline/fdblock.c34
-rw-r--r--src/execline/fdclose.c17
-rw-r--r--src/execline/fdmove.c35
-rw-r--r--src/execline/fdreserve.c64
-rw-r--r--src/execline/forbacktickx.c145
-rw-r--r--src/execline/foreground.c14
-rw-r--r--src/execline/forx.c91
-rw-r--r--src/execline/getpid.c29
-rw-r--r--src/execline/heredoc.c59
-rw-r--r--src/execline/homeof.c28
-rw-r--r--src/execline/if.c54
-rw-r--r--src/execline/ifelse.c54
-rw-r--r--src/execline/ifte.c59
-rw-r--r--src/execline/ifthenelse.c77
-rw-r--r--src/execline/import.c12
-rw-r--r--src/execline/importas.c12
-rw-r--r--src/execline/loopwhilex.c62
-rw-r--r--src/execline/multidefine.c12
-rw-r--r--src/execline/multisubstitute.c64
-rw-r--r--src/execline/pipeline.c85
-rw-r--r--src/execline/piperw.c29
-rw-r--r--src/execline/redirfd.c69
-rw-r--r--src/execline/runblock.c149
-rw-r--r--src/execline/shift.c121
-rw-r--r--src/execline/tryexec.c61
-rw-r--r--src/execline/umask.c20
-rw-r--r--src/execline/unexport.c20
-rw-r--r--src/execline/wait.c62
-rw-r--r--src/include-local/exlsn.h36
-rw-r--r--src/include/execline/execline.h67
-rwxr-xr-xsrc/libexecline/deps-lib/execline21
-rw-r--r--src/libexecline/el_execsequence.c41
-rw-r--r--src/libexecline/el_getstrict.c18
-rw-r--r--src/libexecline/el_obsolescent.c10
-rw-r--r--src/libexecline/el_popenv.c44
-rw-r--r--src/libexecline/el_pushenv.c49
-rw-r--r--src/libexecline/el_semicolon.c35
-rw-r--r--src/libexecline/el_spawn0.c15
-rw-r--r--src/libexecline/el_spawn1.c15
-rw-r--r--src/libexecline/el_substandrun.c13
-rw-r--r--src/libexecline/el_substandrun_str.c25
-rw-r--r--src/libexecline/el_substitute.c179
-rw-r--r--src/libexecline/el_transform.c84
-rw-r--r--src/libexecline/el_vardupl.c12
-rw-r--r--src/libexecline/exlp.c78
-rw-r--r--src/libexecline/exlsn_define.c50
-rw-r--r--src/libexecline/exlsn_elglob.c78
-rw-r--r--src/libexecline/exlsn_exlp.c27
-rw-r--r--src/libexecline/exlsn_free.c12
-rw-r--r--src/libexecline/exlsn_import.c76
-rw-r--r--src/libexecline/exlsn_main.c19
-rw-r--r--src/libexecline/exlsn_multidefine.c79
106 files changed, 3541 insertions, 0 deletions
diff --git a/src/execline/background.c b/src/execline/background.c
new file mode 100644
index 0000000..d3e96ff
--- /dev/null
+++ b/src/execline/background.c
@@ -0,0 +1,76 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef EXECLINE_OLD_VARNAMES
+#include <skalibs/bytestr.h>
+#endif
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/env.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/uint64.h>
+#include <execline/execline.h>
+
+#define USAGE "background [ -d ] { command... }"
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ pid_t pid ;
+ int argc1 ;
+ int df = 0 ;
+ PROG = "background" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "d", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'd' : df = 1 ; break ;
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ argc1 = el_semicolon(argv) ;
+ if (!argc1) strerr_dief1x(100, "empty block") ;
+ if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ;
+ if (argc1 + 1 == argc) df = 0 ;
+ argv[argc1] = 0 ;
+
+ if (df)
+ {
+ pid = doublefork() ;
+ switch (pid)
+ {
+ case -1: strerr_diefu1sys(111, "doublefork") ;
+ case 0:
+ PROG = "background (grandchild)" ;
+ pathexec0_run(argv, envp) ;
+ strerr_dieexec(127, argv[0]) ;
+ }
+ }
+ else
+ {
+ pid = el_spawn0(argv[0], argv, envp) ;
+ if (!pid) strerr_diefu2sys(111, "spawn ", argv[0]) ;
+ }
+ if (argc1 + 1 == argc) return 0 ;
+ {
+#ifdef EXECLINE_OLD_VARNAMES
+ char fmt[UINT64_FMT * 2 + 10] = "!=" ;
+#else
+ char fmt[UINT64_FMT + 2] = "!=" ;
+#endif
+ register unsigned int i = 2 ;
+ i += uint64_fmt(fmt+i, (uint64)pid) ; fmt[i++] = 0 ;
+#ifdef EXECLINE_OLD_VARNAMES
+ byte_copy(fmt+i, 8, "LASTPID=") ; i += 8 ;
+ i += uint64_fmt(fmt+i, (uint64)pid) ; fmt[i++] = 0 ;
+#endif
+ pathexec_r(argv + argc1 + 1, envp, env_len(envp), fmt, i) ;
+ }
+ strerr_dieexec(111, argv[argc1+1]) ;
+}
diff --git a/src/execline/backtick.c b/src/execline/backtick.c
new file mode 100644
index 0000000..d0f74e4
--- /dev/null
+++ b/src/execline/backtick.c
@@ -0,0 +1,84 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/djbunix.h>
+#include <execline/execline.h>
+
+#define USAGE "backtick [ -i ] [ -n ] var { prog... } remainder..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ subgetopt_t localopt = SUBGETOPT_ZERO ;
+ int argc1 ;
+ stralloc modif = STRALLOC_ZERO ;
+ int insist = 0, chomp = 0 ;
+ PROG = "backtick" ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "ein", &localopt) ;
+ if (opt < 0) break ;
+ switch (opt)
+ {
+ case 'i' : insist = 1 ; break ;
+ case 'n' : chomp = 1 ; break ;
+ case 'e' : break ; /* compat */
+ default : dieusage() ;
+ }
+ }
+ argc -= localopt.ind ; argv += localopt.ind ;
+
+ if (argc < 2) dieusage() ;
+ if (!*argv[0]) strerr_dief1x(100, "empty variable not accepted") ;
+ if (!stralloc_cats(&modif, argv[0]) || !stralloc_catb(&modif, "=", 1))
+ strerr_diefu1sys(111, "stralloc_catb") ;
+ argc-- ; argv++ ;
+ argc1 = el_semicolon(argv) ;
+ if (!argc1) strerr_dief1x(100, "empty block") ;
+ if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ;
+
+ {
+ int p[2] ;
+ pid_t pid ;
+ if (pipe(p) < 0) strerr_diefu1sys(111, "pipe") ;
+ pid = fork() ;
+ switch (pid)
+ {
+ case -1: strerr_diefu1sys(111, "fork") ;
+ case 0:
+ argv[argc1] = 0 ;
+ fd_close(p[0]) ;
+ PROG = "backtick (child)" ;
+ if (fd_move(1, p[1]) < 0) strerr_diefu1sys(111, "fd_move") ;
+ pathexec_run(argv[0], argv, envp) ;
+ strerr_dieexec(111, argv[0]) ;
+ }
+ fd_close(p[1]) ;
+ if (!slurp(&modif, p[0])) strerr_diefu1sys(111, "slurp") ;
+ fd_close(p[0]) ;
+ if (wait_pid(pid, &p[0]) < 0) strerr_diefu1sys(111, "wait_pid") ;
+ if (insist && wait_status(p[0]))
+ strerr_dief1x(wait_status(p[0]), "child process exited non-zero") ;
+ }
+ if (argc == argc1 - 1) return 0 ;
+ if (!stralloc_0(&modif)) strerr_diefu1sys(111, "stralloc_catb") ;
+ {
+ unsigned int reallen = str_len(modif.s) ;
+ if (reallen < modif.len - 1)
+ {
+ if (insist)
+ strerr_dief1x(1, "child process output contained a null character") ;
+ else
+ modif.len = reallen + 1 ;
+ }
+ if (chomp && (modif.s[modif.len - 2] == '\n'))
+ modif.s[--modif.len - 1] = 0 ;
+ }
+ pathexec_r(argv + argc1 + 1, envp, env_len(envp), modif.s, modif.len) ;
+ strerr_dieexec(111, argv[argc1 + 1]) ;
+}
diff --git a/src/execline/cd.c b/src/execline/cd.c
new file mode 100644
index 0000000..c774ce5
--- /dev/null
+++ b/src/execline/cd.c
@@ -0,0 +1,17 @@
+/* ISC license. */
+
+#include <unistd.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "cd path prog..."
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ PROG = "cd" ;
+ if (argc < 3) strerr_dieusage(100, USAGE) ;
+ if (chdir(argv[1]) == -1)
+ strerr_diefu2sys(111, "chdir to ", argv[1]) ;
+ pathexec_run(argv[2], argv+2, envp) ;
+ strerr_dieexec(111, argv[2]) ;
+}
diff --git a/src/execline/define.c b/src/execline/define.c
new file mode 100644
index 0000000..f87c531
--- /dev/null
+++ b/src/execline/define.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/strerr2.h>
+#include "exlsn.h"
+
+#define USAGE "define [ -n ] [ -s ] [ -C | -c ] [ -d delim ] key value prog..."
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ PROG = "define" ;
+ exlsn_main(argc, argv, envp, &exlsn_define, USAGE) ;
+}
diff --git a/src/execline/deps-exe/background b/src/execline/deps-exe/background
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/background
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/backtick b/src/execline/deps-exe/backtick
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/backtick
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/cd b/src/execline/deps-exe/cd
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/execline/deps-exe/cd
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/execline/deps-exe/define b/src/execline/deps-exe/define
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/define
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/dollarat b/src/execline/deps-exe/dollarat
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/execline/deps-exe/dollarat
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/execline/deps-exe/elgetopt b/src/execline/deps-exe/elgetopt
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/elgetopt
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/elgetpositionals b/src/execline/deps-exe/elgetpositionals
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/elgetpositionals
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/elglob b/src/execline/deps-exe/elglob
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/elglob
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/emptyenv b/src/execline/deps-exe/emptyenv
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/emptyenv
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/exec b/src/execline/deps-exe/exec
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/execline/deps-exe/exec
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/execline/deps-exe/execlineb b/src/execline/deps-exe/execlineb
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/execlineb
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/exit b/src/execline/deps-exe/exit
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/execline/deps-exe/exit
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/execline/deps-exe/export b/src/execline/deps-exe/export
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/execline/deps-exe/export
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/execline/deps-exe/fdblock b/src/execline/deps-exe/fdblock
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/execline/deps-exe/fdblock
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/execline/deps-exe/fdclose b/src/execline/deps-exe/fdclose
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/execline/deps-exe/fdclose
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/execline/deps-exe/fdmove b/src/execline/deps-exe/fdmove
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/execline/deps-exe/fdmove
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/execline/deps-exe/fdreserve b/src/execline/deps-exe/fdreserve
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/execline/deps-exe/fdreserve
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/execline/deps-exe/forbacktickx b/src/execline/deps-exe/forbacktickx
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/forbacktickx
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/foreground b/src/execline/deps-exe/foreground
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/foreground
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/forx b/src/execline/deps-exe/forx
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/forx
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/getpid b/src/execline/deps-exe/getpid
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/execline/deps-exe/getpid
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/execline/deps-exe/heredoc b/src/execline/deps-exe/heredoc
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/execline/deps-exe/heredoc
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/execline/deps-exe/homeof b/src/execline/deps-exe/homeof
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/execline/deps-exe/homeof
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/execline/deps-exe/if b/src/execline/deps-exe/if
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/if
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/ifelse b/src/execline/deps-exe/ifelse
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/ifelse
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/ifte b/src/execline/deps-exe/ifte
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/ifte
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/ifthenelse b/src/execline/deps-exe/ifthenelse
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/ifthenelse
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/import b/src/execline/deps-exe/import
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/import
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/importas b/src/execline/deps-exe/importas
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/importas
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/loopwhilex b/src/execline/deps-exe/loopwhilex
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/loopwhilex
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/multidefine b/src/execline/deps-exe/multidefine
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/multidefine
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/multisubstitute b/src/execline/deps-exe/multisubstitute
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/multisubstitute
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/pipeline b/src/execline/deps-exe/pipeline
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/pipeline
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/piperw b/src/execline/deps-exe/piperw
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/execline/deps-exe/piperw
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/execline/deps-exe/redirfd b/src/execline/deps-exe/redirfd
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/execline/deps-exe/redirfd
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/execline/deps-exe/runblock b/src/execline/deps-exe/runblock
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/runblock
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/shift b/src/execline/deps-exe/shift
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/shift
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/tryexec b/src/execline/deps-exe/tryexec
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/tryexec
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/deps-exe/umask b/src/execline/deps-exe/umask
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/execline/deps-exe/umask
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/execline/deps-exe/unexport b/src/execline/deps-exe/unexport
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/execline/deps-exe/unexport
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/execline/deps-exe/wait b/src/execline/deps-exe/wait
new file mode 100644
index 0000000..8c3ba3b
--- /dev/null
+++ b/src/execline/deps-exe/wait
@@ -0,0 +1,2 @@
+-lexecline
+-lskarnet
diff --git a/src/execline/dollarat.c b/src/execline/dollarat.c
new file mode 100644
index 0000000..66b5c61
--- /dev/null
+++ b/src/execline/dollarat.c
@@ -0,0 +1,63 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/buffer.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/uint.h>
+#include <skalibs/netstring.h>
+
+#define USAGE "dollarat [ -n ] [ -0 | -d delimchar ]"
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ unsigned int n, i = 0 ;
+ char const *x ;
+ char delim = '\n' ;
+ int zero = 0 ;
+ int nl = 1 ;
+ PROG = "dollarat" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "nd:0", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'n' : nl = 0 ; break ;
+ case 'd' : delim = *l.arg ; break ;
+ case '0' : zero = 1 ; break ;
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ if (zero) delim = 0 ;
+ x = env_get2(envp, "#") ;
+ if (!x) strerr_dienotset(100, "#") ;
+ if (!uint0_scan(x, &n)) strerr_dieinvalid(100, "#") ;
+ for (; i < n ; i++)
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, i+1)] = 0 ;
+ x = env_get2(envp, fmt) ;
+ if (!x) strerr_dienotset(100, fmt) ;
+ if (delim || zero)
+ {
+ if ((buffer_puts(buffer_1, x) < 0)
+ || (((i < n-1) || nl) && (buffer_put(buffer_1, &delim, 1) < 0)))
+ strerr_diefu1sys(111, "write to stdout") ;
+ }
+ else
+ {
+ unsigned int written = 0 ;
+ if (!netstring_put(buffer_1, x, str_len(x), &written))
+ strerr_diefu1sys(111, "write a netstring to stdout") ;
+ }
+ }
+ if (!buffer_flush(buffer_1))
+ strerr_diefu1sys(111, "write to stdout") ;
+ return 0 ;
+}
diff --git a/src/execline/elgetopt.c b/src/execline/elgetopt.c
new file mode 100644
index 0000000..4dfd4d8
--- /dev/null
+++ b/src/execline/elgetopt.c
@@ -0,0 +1,69 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/env.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/skamisc.h>
+#include <skalibs/uint.h>
+#include <execline/execline.h>
+
+#define USAGE "elgetopt optstring prog..."
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ unsigned int n, nbak ;
+ unsigned int envlen = env_len(envp) ;
+ stralloc modif = STRALLOC_ZERO ;
+ char const *x = env_get2(envp, "#") ;
+ PROG = "elgetopt" ;
+ if (argc < 3) strerr_dieusage(100, USAGE) ;
+ if (!x) strerr_dienotset(100, "#") ;
+ if (!uint0_scan(x, &n)) strerr_dieinvalid(100, "#") ;
+ nbak = n++ ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ char const *args[n+1] ;
+ register unsigned int i = 0 ;
+ for ( ; i < n ; i++)
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, i)] = 0 ;
+ args[i] = env_get2(envp, fmt) ;
+ if (!args[i]) strerr_dienotset(100, fmt) ;
+ }
+ args[n] = 0 ;
+ for (;;)
+ {
+ char hmpf[11] = "ELGETOPT_?" ;
+ register int opt = sgetopt_r(n, args, argv[1], &l) ;
+ if (opt == -1) break ;
+ if (opt == '?') return 1 ;
+ hmpf[9] = opt ;
+ if (!env_addmodif(&modif, hmpf, l.arg ? l.arg : "1")) goto err ;
+ }
+ n -= l.ind ;
+ for (i = 0 ; i < nbak ; i++)
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, i+1)] = 0 ;
+ if (!env_addmodif(&modif, fmt, (i < n) ? args[l.ind + i] : 0)) goto err ;
+ }
+ }
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, n)] = 0 ;
+ if (!env_addmodif(&modif, "#", fmt)) goto err ;
+ }
+ {
+ char const *const list[1] = { "ELGETOPT_" } ;
+ char const *v[envlen] ;
+ if (el_pushenv(&satmp, envp, envlen, list, 1) < 0) goto err ;
+ if (!env_make(v, envlen, satmp.s, satmp.len)) goto err ;
+ pathexec_r(argv+2, v, envlen, modif.s, modif.len) ;
+ strerr_dieexec(111, argv[2]) ;
+ }
+err:
+ strerr_diefu1sys(111, "update environment") ;
+}
diff --git a/src/execline/elgetpositionals.c b/src/execline/elgetpositionals.c
new file mode 100644
index 0000000..3ce4b69
--- /dev/null
+++ b/src/execline/elgetpositionals.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/strerr2.h>
+#include "exlsn.h"
+
+#define USAGE "elgetpositionals [ -P num ] prog..."
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ PROG = "elgetpositionals" ;
+ exlsn_main(argc, argv, envp, &exlsn_exlp, USAGE) ;
+}
diff --git a/src/execline/elglob.c b/src/execline/elglob.c
new file mode 100644
index 0000000..ffff74a
--- /dev/null
+++ b/src/execline/elglob.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/strerr2.h>
+#include "exlsn.h"
+
+#define USAGE "elglob [ -v ] [ -w ] [ -s ] [ -m ] [ -e ] [ -0 ] key pattern prog..."
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ PROG = "elglob" ;
+ exlsn_main(argc, argv, envp, &exlsn_elglob, USAGE) ;
+}
diff --git a/src/execline/emptyenv.c b/src/execline/emptyenv.c
new file mode 100644
index 0000000..cae8869
--- /dev/null
+++ b/src/execline/emptyenv.c
@@ -0,0 +1,92 @@
+/* ISC license. */
+
+#include <skalibs/sgetopt.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/env.h>
+#include <skalibs/djbunix.h>
+#include <execline/execline.h>
+
+#define USAGE "emptyenv [ -p | -c | -o | -P ] prog..."
+
+static void cleanupenv (char const *const *argv, char const *const *envp)
+{
+ stralloc sa = STRALLOC_ZERO ;
+ if (!pathexec_env("!", 0) || !pathexec_env("?", 0)) goto err ;
+#ifdef EXECLINE_OLD_VARNAMES
+ if (!pathexec_env("LASTPID", 0) || !pathexec_env("LASTEXITCODE", 0)) goto err ;
+#endif
+ for (; *envp ; envp++)
+ {
+ char const *s = *envp ;
+ sa.len = 0 ;
+ if (!str_diffn(s, "ELGETOPT_", 9)
+ || !str_diffn(s, "EXECLINE_", 9)
+ || !str_diffn(s, "FD", 2)
+ || (s[0] == '#')
+ || ((s[0] >= '0') && (s[0] <= '9')))
+ if (!stralloc_catb(&sa, s, str_chr(s, '='))
+ || !stralloc_0(&sa)
+ || !pathexec_env(sa.s, 0))
+ goto err ;
+ }
+ stralloc_free(&sa) ;
+ pathexec(argv) ;
+ strerr_dieexec(111, argv[0]) ;
+err:
+ strerr_diefu1sys(111, "clean up environment") ;
+}
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ int flagpath = 0, flagcleanup = 0, flagopt = 0, flagpos = 0 ;
+ PROG = "emptyenv" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "pcoP", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'p' : flagpath = 1 ; break ;
+ case 'c' : flagcleanup = 1 ; break ;
+ case 'o' : flagopt = 1 ; break ;
+ case 'P' : flagpos = 1 ; break ;
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ if (!argc) strerr_dieusage(100, USAGE) ;
+ if (flagcleanup) cleanupenv(argv, envp) ;
+ else if (!flagopt && !flagpos)
+ {
+ char const *newenv[2] = { 0, 0 } ;
+ if (flagpath)
+ for (; *envp ; envp++)
+ if (!str_diffn(*envp, "PATH=", 5))
+ {
+ newenv[0] = *envp ;
+ break ;
+ }
+ pathexec_run(argv[0], argv, newenv) ;
+ }
+ else
+ {
+ static char const *const list[12] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "#", "ELGETOPT_" } ;
+ stralloc sa = STRALLOC_ZERO ;
+ unsigned int envlen = env_len(envp) ;
+ int n = el_popenv(&sa, envp, envlen, flagpos ? list : list + 11, 11 * flagpos + flagopt) ;
+ if (n < 0) strerr_diefu1sys(111, "pop current execline environment") ;
+ {
+ char const *v[envlen - n + 1] ;
+ if (!env_make(v, envlen-n, sa.s, sa.len)) strerr_diefu1sys(111, "env_make") ;
+ v[envlen-n] = 0 ;
+ pathexec_run(argv[0], argv, v) ;
+ }
+ stralloc_free(&sa) ;
+ }
+ strerr_dieexec(111, argv[0]) ;
+}
diff --git a/src/execline/exec.c b/src/execline/exec.c
new file mode 100644
index 0000000..2213ba4
--- /dev/null
+++ b/src/execline/exec.c
@@ -0,0 +1,48 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+
+#define USAGE "exec [ -c ] [ -l ] [ -a argv0 ] prog..."
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ static char const *const zero = 0 ;
+ char const *executable = 0 ;
+ char const *argv0 = 0 ;
+ int dash = 0 ;
+ PROG = "exec" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "cla:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'c' : envp = &zero ; break ;
+ case 'l' : dash = 1 ; break ;
+ case 'a' : argv0 = l.arg ; break ;
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ if (!argc) strerr_dieusage(100, USAGE) ;
+
+ executable = argv[0] ;
+ if (argv0) argv[0] = argv0 ;
+ if (dash)
+ {
+ register unsigned int n = str_len(argv[0]) ;
+ char dashed[n+2] ;
+ dashed[0] = '-' ;
+ byte_copy(dashed+1, n+1, argv[0]) ;
+ argv[0] = (char const *)dashed ;
+ pathexec_run(executable, argv, envp) ;
+ }
+ else pathexec_run(executable, argv, envp) ;
+ strerr_dieexec(111, executable) ;
+}
diff --git a/src/execline/execlineb.c b/src/execline/execlineb.c
new file mode 100644
index 0000000..1b9e7ad
--- /dev/null
+++ b/src/execline/execlineb.c
@@ -0,0 +1,327 @@
+/* ISC license. */
+
+#include <skalibs/uint16.h>
+#include <skalibs/uint.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/buffer.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/skamisc.h>
+#include <execline/execline.h>
+#include "exlsn.h"
+
+#define USAGE "execlineb [ -p | -P | -S nmin ] [ -q | -w | -W ] [ -c commandline ] script args"
+
+typedef unsigned char chargen_t (void) ;
+
+/* Action (strongest 11 bits) */
+
+#define PUSH 0x8000
+#define PUSH0 0x4000
+#define PUSHSPECIAL 0x2000
+#define SETBASE 0x1000
+#define MARK 0x0800
+#define CALC 0x0400
+#define QUOTE 0x0200
+#define INCB 0x0100
+#define DECB 0x0080
+
+
+/* State (weakest 5 bits) */
+
+#define MAIN 0x00
+#define INWORD 0x01
+#define INWORDESC 0x02
+#define INSTR 0x03
+#define INSTRESC 0x04
+#define INREM 0x05
+#define OCT0 0x06
+#define OCT1 0x07
+#define OCT2 0x08
+#define DEC1 0x09
+#define DEC2 0x0a
+#define HEX0 0x0b
+#define HEX1 0x0c
+#define ENDCALC 0x0d
+#define OPENB 0x0e
+#define CLOSEB 0x0f
+#define ERROR 0x10
+#define ACCEPT 0x11
+
+static buffer b ;
+
+static void initbuffer (char const *s)
+{
+ static char buf[BUFFER_INSIZE] ;
+ int fd = open_readb(s) ;
+ if (fd < 0) strerr_diefu3sys(111, "open ", s, " for reading") ;
+ if (coe(fd) < 0) strerr_diefu2sys(111, "coe ", s) ;
+ buffer_init(&b, &buffer_read, fd, buf, BUFFER_INSIZE) ;
+}
+
+static unsigned char nextinbuffer ()
+{
+ char c ;
+ switch (buffer_get(&b, &c, 1))
+ {
+ case -1: strerr_diefu1sys(111, "read script") ;
+ case 0 : return 0 ;
+ }
+ return (unsigned char)c ;
+}
+
+static unsigned char const *string = 0 ;
+
+static unsigned char nextinstring ()
+{
+ static unsigned int pos = 0 ;
+ return string[pos++] ;
+}
+
+static int lex (stralloc *sa, chargen_t *next)
+{
+ static unsigned char const class[256] = "`aaaaaaaaadaaaaaaaaaaaaaaaaaaaaaafcbffffffffffffjhhhhhhhiifffffffmmmmmmfffffffffffffffffffffeffffggmmmgfffffffkfffkfkfkflffnfoffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" ;
+ static uint16 const table[16][16] =
+ {
+ { 0x0011, 0x4011, 0x0010, 0x0010, 0x0010, 0x0011, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x0010, 0x4091 },
+ { 0x0000, 0x4000, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x0100, 0x4080 },
+ { 0x0005, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 },
+ { 0x0203, 0x0003, 0x8001, 0x0001, 0x8003, 0x0005, 0x0010, 0x0401, 0x0401, 0x0401, 0x0401, 0x0010, 0x0401, 0x0401, 0x0003, 0x0003 },
+ { 0x0000, 0x4000, 0x8001, 0x8003, 0x0003, 0x0000, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x0100, 0x4080 },
+ { 0x0202, 0x0002, 0x8001, 0x0004, 0x8003, 0x0005, 0x0010, 0x0404, 0x0404, 0x0404, 0x0404, 0x0010, 0x0404, 0x0404, 0x0002, 0x0002 },
+ { 0x8201, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 },
+ { 0x8201, 0x8001, 0x8001, 0x8003, 0x2003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 },
+ { 0x8201, 0x8001, 0x8001, 0x8003, 0x9809, 0x0005, 0x8807, 0x8008, 0x800d, 0x800a, 0x800d, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 },
+ { 0x8201, 0x8001, 0x8001, 0x8003, 0x9809, 0x0005, 0x0010, 0x8403, 0x8403, 0x800a, 0x800d, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 },
+ { 0x8201, 0x8001, 0x8001, 0x8003, 0x1006, 0x0005, 0x8807, 0x8008, 0x800d, 0x800a, 0x800d, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 },
+ { 0x8201, 0x8001, 0x8001, 0x8003, 0x2003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 },
+ { 0x8201, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x100b, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 },
+ { 0x8201, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x880c, 0x800d, 0x8403, 0x8001, 0x8001 },
+ { 0x820e, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 },
+ { 0x820f, 0x8001, 0x8001, 0x8003, 0x8003, 0x0005, 0x0010, 0x8403, 0x8403, 0x8403, 0x8403, 0x0010, 0x8403, 0x8403, 0x8001, 0x8001 }
+ } ;
+
+ unsigned int mark = 0 ;
+ unsigned int n = 0 ;
+ unsigned char state = MAIN, base = 10 ;
+ unsigned int blevel = 0 ;
+
+ while (state < ERROR)
+ {
+ unsigned char cur = (*next)() ;
+ register uint16 c = table[class[cur]-'`'][state] ;
+ state = c & 0x1F ;
+
+ /* Actions. The order is important ! */
+
+ if (c & CALC)
+ {
+ unsigned int z ;
+ if (!stralloc_0(sa)) return -1 ;
+ sa->len = mark ;
+ uint_scan_base(sa->s + sa->len, &z, base) ;
+ sa->s[sa->len++] = (unsigned char)z ;
+ }
+ if (c & MARK) mark = sa->len ;
+ if (c & QUOTE)
+ {
+ char tilde = EXECLINE_BLOCK_QUOTE_CHAR ;
+ register unsigned int i = blevel ;
+ if (!stralloc_readyplus(sa, i<<1)) return -1 ;
+ while (i--) stralloc_catb(sa, &tilde, 1) ;
+ }
+ if (c & INCB) sa->len -= ++blevel ;
+ if (c & DECB)
+ {
+ if (!blevel--) return -4 ;
+ sa->s[--sa->len-1] = EXECLINE_BLOCK_END_CHAR ;
+ if (!EXECLINE_BLOCK_END_CHAR) sa->len-- ;
+ }
+ if (c & PUSH) if (!stralloc_catb(sa, (char *)&cur, 1)) return -1 ;
+ if (c & PUSHSPECIAL)
+ {
+ char x = 7 + byte_chr("abtnvfr", 7, cur) ;
+ if (!stralloc_catb(sa, &x, 1)) return -1 ;
+ }
+ if (c & PUSH0) if (n++, !stralloc_0(sa)) return -1 ;
+ if (c & SETBASE)
+ switch (cur)
+ {
+ case 'x' : base = 16 ; break ;
+ case '0' : base = 8 ; break ;
+ default : base = 10 ;
+ }
+ }
+ if (state == ERROR) return -2 ;
+ if (blevel) return -3 ;
+ return n ;
+}
+
+
+static int myexlp (stralloc *sa, char const *const *argv, unsigned int argc, unsigned int nmin, char const *dollar0)
+{
+ exlsn_t info = EXLSN_ZERO ;
+ unsigned int n = argc > nmin ? argc : nmin ;
+ unsigned int i = 0 ;
+
+ if (!genalloc_ready(elsubst_t, &info.data, 3 + n)) return -1 ;
+ if (!stralloc_ready(&info.vars, 6 + (n << 1))) goto err ;
+ stralloc_catb(&info.vars, "#\0" "0\0@", 6) ;
+ {
+ elsubst_t blah[3] ;
+ char fmt[UINT_FMT] ;
+ blah[0].var = 0 ; blah[0].value = 0 ; blah[0].n = 1 ;
+ if (!stralloc_catb(&info.values, fmt, uint_fmt(fmt, argc)) || !stralloc_0(&info.values)) goto err ;
+ blah[1].var = 2 ; blah[1].value = info.values.len ; blah[1].n = 1 ;
+ if (!stralloc_catb(&info.values, dollar0, str_len(dollar0) + 1)) goto err ;
+ blah[2].var = 4 ; blah[2].value = info.values.len ; blah[2].n = argc ;
+ genalloc_catb(elsubst_t, &info.data, blah, 3) ;
+ }
+ for (; i < n ; i++)
+ {
+ elsubst_t blah ;
+ char fmt[UINT_FMT] ;
+ blah.var = info.vars.len ; blah.value = info.values.len ; blah.n = 1 ;
+ if (!stralloc_catb(&info.vars, fmt, uint_fmt(fmt, i+1)) || !stralloc_0(&info.vars)) goto err ;
+ if (!stralloc_catb(&info.values, i < argc ? argv[i] : "", i < argc ? str_len(argv[i]) + 1 : 1)) goto err ;
+ genalloc_append(elsubst_t, &info.data, &blah) ;
+ }
+ {
+ stralloc dst = STRALLOC_ZERO ;
+ int r = el_substitute(&dst, sa->s, sa->len, info.vars.s, info.values.s, genalloc_s(elsubst_t, &info.data), genalloc_len(elsubst_t, &info.data)) ;
+ if (r < 0) goto err ;
+ exlsn_free(&info) ;
+ stralloc_free(sa) ;
+ *sa = dst ;
+ return r ;
+ }
+
+ err:
+ exlsn_free(&info) ;
+ return -1 ;
+}
+
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ chargen_t *next ;
+ stralloc sa = STRALLOC_ZERO ;
+ stralloc modif = STRALLOC_ZERO ;
+ int nc ;
+ int flagstrict = -1 ;
+ unsigned int nmin = 0 ;
+ char const *dollar0 = argv[0] ;
+ unsigned int flagpushenv = 2 ;
+ PROG = "execlineb" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "pPqwWc:S:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'p' : flagpushenv = 1 ; break ;
+ case 'P' : flagpushenv = 0 ; break ;
+ case 'q' : flagstrict = 0 ; break ;
+ case 'w' : flagstrict = 1 ; break ;
+ case 'W' : flagstrict = 2 ; break ;
+ case 'c' : string = (unsigned char *)l.arg ; break ;
+ case 'S' :
+ {
+ if (!uint0_scan(l.arg, &nmin)) strerr_dieusage(100, USAGE) ;
+ flagpushenv = 3 ;
+ break ;
+ }
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ if (string) next = &nextinstring ;
+ else
+ {
+ if (!argv[0]) strerr_dieusage(100, USAGE) ;
+ initbuffer(argv[0]) ;
+ dollar0 = argv[0] ;
+ argv++ ; argc-- ;
+ next = &nextinbuffer ;
+ }
+
+ nc = lex(&sa, next) ;
+ switch (nc)
+ {
+ case -4: strerr_dief2x(100, "unmatched ", "}") ;
+ case -3: strerr_dief2x(100, "unmatched ", "{") ;
+ case -2: strerr_dief1x(100, "syntax error") ;
+ case -1: strerr_diefu1sys(111, "parse script") ;
+ case 0 : return 0 ;
+ }
+
+ if (flagstrict >= 0)
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, (unsigned int)flagstrict)] = 0 ;
+ if (!env_addmodif(&modif, "EXECLINE_STRICT", flagstrict ? fmt : 0)) goto errenv ;
+ }
+
+ if (flagpushenv == 3)
+ {
+ flagpushenv = 0 ;
+ if (flagstrict && ((unsigned int)argc < nmin))
+ {
+ char fmtn[UINT_FMT] ;
+ char fmta[UINT_FMT] ;
+ fmtn[uint_fmt(fmtn, nmin)] = 0 ;
+ fmta[uint_fmt(fmta, argc)] = 0 ;
+ if (flagstrict > 1)
+ strerr_dief4x(100, "too few arguments: expecting at least ", fmtn, " but got ", fmta) ;
+ else
+ strerr_warnw4x("too few arguments: expecting at least ", fmtn, " but got ", fmta) ;
+ }
+ nc = myexlp(&sa, argv, argc, nmin, dollar0) ;
+ if (nc < 0) strerr_diefu1sys(111, "substitute positional parameters") ;
+ }
+ else if (flagpushenv)
+ {
+ char fmt[UINT_FMT] ;
+ register unsigned int i = 0 ;
+ fmt[uint_fmt(fmt, argc)] = 0 ;
+ if (!env_addmodif(&modif, "#", fmt)) goto errenv ;
+ if (!env_addmodif(&modif, "0", dollar0)) goto errenv ;
+ for (; i < (unsigned int)argc ; i++)
+ {
+ fmt[uint_fmt(fmt, i+1)] = 0 ;
+ if (!env_addmodif(&modif, fmt, argv[i])) goto errenv ;
+ }
+ }
+
+ {
+ char const *v[nc+1] ;
+ if (!env_make(v, nc, sa.s, sa.len)) strerr_diefu1sys(111, "make argv") ;
+ v[nc] = 0 ;
+
+ if (flagpushenv > 1)
+ {
+ static char const *const list[11] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "#" } ;
+ unsigned int envlen = env_len(envp) ;
+ char const *w[envlen] ;
+ if (el_pushenv(&satmp, envp, envlen, list, 11) < 0) goto errenv ;
+ if (!env_make(w, envlen, satmp.s, satmp.len)) goto errenv ;
+ pathexec_r(v, w, envlen, modif.s, modif.len) ;
+ stralloc_free(&satmp) ;
+ }
+ else if (modif.len)
+ pathexec_r(v, envp, env_len(envp), modif.s, modif.len) ;
+ else
+ pathexec_run(v[0], v, envp) ;
+ }
+ stralloc_free(&modif) ;
+ strerr_dieexec(111, sa.s) ;
+errenv:
+ strerr_diefu1sys(111, "update environment") ;
+}
diff --git a/src/execline/exit.c b/src/execline/exit.c
new file mode 100644
index 0000000..d9c6c20
--- /dev/null
+++ b/src/execline/exit.c
@@ -0,0 +1,15 @@
+/* ISC license. */
+
+#include <skalibs/strerr2.h>
+#include <skalibs/uint.h>
+
+#define USAGE "exit [ exitcode ]"
+
+int main (int argc, char const *const *argv)
+{
+ unsigned int e ;
+ PROG = "exit" ;
+ if (argc < 2) return 0 ;
+ if (!uint0_scan(argv[1], &e)) strerr_dieusage(100, USAGE) ;
+ return (int)e ;
+}
diff --git a/src/execline/export.c b/src/execline/export.c
new file mode 100644
index 0000000..e7a7bbe
--- /dev/null
+++ b/src/execline/export.c
@@ -0,0 +1,27 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/env.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "export variable value prog..."
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ unsigned int len1 ;
+ PROG = "export" ;
+ if (argc < 4) strerr_dieusage(100, USAGE) ;
+ len1 = str_len(argv[1]) ;
+ if (byte_chr(argv[1], len1, '=') < len1)
+ strerr_dief2x(100, "invalid variable name: ", argv[1]) ;
+ {
+ unsigned int len2 = str_len(argv[2]) ;
+ char fmt[len1 + len2 + 2] ;
+ byte_copy(fmt, len1, argv[1]) ;
+ fmt[len1] = '=' ;
+ byte_copy(fmt + len1 + 1, len2 + 1, argv[2]) ;
+ pathexec_r(argv+3, envp, env_len(envp), fmt, len1 + len2 + 2) ;
+ }
+ strerr_dieexec(111, argv[3]) ;
+}
diff --git a/src/execline/fdblock.c b/src/execline/fdblock.c
new file mode 100644
index 0000000..486591e
--- /dev/null
+++ b/src/execline/fdblock.c
@@ -0,0 +1,34 @@
+/* ISC license. */
+
+#include <skalibs/uint.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "fdblock [ -n ] fd prog..."
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ unsigned int fd ;
+ int block = 1 ;
+ PROG = "fdblock" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "n", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'n' : block = 0 ; break ;
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ if ((argc < 2) || !uint0_scan(argv[0], &fd)) strerr_dieusage(100, USAGE) ;
+ if ((block ? ndelay_off(fd) : ndelay_on(fd)) < 0)
+ strerr_diefu1sys(111, block ? "ndelay_off" : "ndelay_on") ;
+ pathexec_run(argv[1], argv+1, envp) ;
+ strerr_dieexec(111, argv[1]) ;
+}
diff --git a/src/execline/fdclose.c b/src/execline/fdclose.c
new file mode 100644
index 0000000..33b5941
--- /dev/null
+++ b/src/execline/fdclose.c
@@ -0,0 +1,17 @@
+/* ISC license. */
+
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "fdclose fd prog..."
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ unsigned int fd ;
+ PROG = "fdclose" ;
+ if ((argc < 3) || !uint0_scan(argv[1], &fd)) strerr_dieusage(100, USAGE) ;
+ fd_close((int)fd) ;
+ pathexec_run(argv[2], argv+2, envp) ;
+ strerr_dieexec(111, argv[2]) ;
+}
diff --git a/src/execline/fdmove.c b/src/execline/fdmove.c
new file mode 100644
index 0000000..8b7417e
--- /dev/null
+++ b/src/execline/fdmove.c
@@ -0,0 +1,35 @@
+/* ISC license. */
+
+#include <skalibs/sgetopt.h>
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "fdmove [ -c ] to from prog..."
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ unsigned int to, from ;
+ int flagcopy = 0 ;
+ PROG = "fdmove" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "c", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'c' : flagcopy = 1 ; break ;
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ if ((argc < 3) || !uint0_scan(argv[0], &to) || !uint0_scan(argv[1], &from))
+ strerr_dieusage(100, USAGE) ;
+ if ((flagcopy ? fd_copy((int)to, (int)from) : fd_move((int)to, (int)from)) == -1)
+ strerr_diefu4sys(111, "move fd ", argv[1], " to fd ", argv[0]) ;
+ pathexec_run(argv[2], argv+2, envp) ;
+ strerr_dieexec(111, argv[2]) ;
+}
diff --git a/src/execline/fdreserve.c b/src/execline/fdreserve.c
new file mode 100644
index 0000000..2df721a
--- /dev/null
+++ b/src/execline/fdreserve.c
@@ -0,0 +1,64 @@
+/* ISC license. */
+
+#include <unistd.h>
+#include <sys/resource.h>
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/env.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "fdreserve n prog..."
+
+#define MAXFDS 1024
+
+unsigned int doit (char *modif, unsigned int i, int fd)
+{
+ register unsigned int pos = 2 ;
+ modif[0] = 'F' ; modif[1] = 'D' ;
+ pos += uint_fmt(modif + pos, i) ;
+ modif[pos++] = '=' ;
+ pos += uint_fmt(modif + pos, (unsigned int)fd) ;
+ modif[pos++] = 0 ;
+ return pos ;
+}
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ unsigned int n ;
+ PROG = "fdreserve" ;
+ if ((argc < 3) || !uint0_scan(argv[1], &n))
+ strerr_dieusage(100, USAGE) ;
+ {
+ struct rlimit lim ;
+ if (getrlimit(RLIMIT_NOFILE, &lim) < 0) strerr_diefu1sys(111, "getrlimit") ;
+ if (n > lim.rlim_cur) strerr_dief1x(100, "too many requested fds") ;
+ }
+ {
+ char modif[12 * n] ; /* enough for n times "FDaaaa=bbbb\0" */
+ unsigned int j = 0 ;
+ {
+ int fd[n >> 1][2] ;
+ register unsigned int i = 0 ;
+ for (; i < (n>>1) ; i++)
+ if (pipe(fd[i]) < 0)
+ strerr_diefu1sys(111, "reserve fds") ;
+ if (n & 1)
+ {
+ register int lastfd = open_read("/dev/null") ;
+ if (lastfd < 0)
+ strerr_diefu1sys(111, "reserve last fd") ;
+ fd_close(lastfd) ;
+ j += doit(modif + j, n-1, lastfd) ;
+ }
+ for (i = 0 ; i < (n>>1) ; i++)
+ {
+ fd_close(fd[i][0]) ;
+ fd_close(fd[i][1]) ;
+ j += doit(modif + j, i<<1, fd[i][0]) ;
+ j += doit(modif + j, (i<<1)|1, fd[i][1]) ;
+ }
+ }
+ pathexec_r(argv+2, envp, env_len(envp), modif, j) ;
+ }
+ strerr_dieexec(111, argv[2]) ;
+}
diff --git a/src/execline/forbacktickx.c b/src/execline/forbacktickx.c
new file mode 100644
index 0000000..f7b1460
--- /dev/null
+++ b/src/execline/forbacktickx.c
@@ -0,0 +1,145 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <errno.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/buffer.h>
+#include <skalibs/fmtscan.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/env.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/skamisc.h>
+#include <skalibs/netstring.h>
+#include <skalibs/ushort.h>
+#include <execline/config.h>
+#include <execline/execline.h>
+
+#define USAGE "forbacktickx [ -p | -x breakcode,breakcode,... ] [ -n ] [ -C | -c ] [ -0 | -d delim ] var { backtickcmd... } command..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+static int isbreak (unsigned short *tab, unsigned int n, int code)
+{
+ register unsigned int i = 0 ;
+ for (; i < n ; i++) if ((unsigned short)code == tab[i]) break ;
+ return i < n ;
+}
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ genalloc pids = GENALLOC_ZERO ; /* pid_t */
+ char const *delim = " \n\r\t" ;
+ unsigned int delimlen = 4 ;
+ char const *x ;
+ int argc1 ;
+ unsigned short breakcodes[256] ;
+ unsigned int nbc = 0 ;
+ int crunch = 0, chomp = 0 ;
+ PROG = "forbacktickx" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "epnCc0d:x:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'e' : break ; /* compat */
+ case 'p' :
+ {
+ if (!genalloc_ready(pid_t, &pids, 2))
+ strerr_diefu1sys(111, "genalloc_ready") ;
+ break ;
+ }
+ case 'n' : chomp = 1 ; break ;
+ case 'C' : crunch = 1 ; break ;
+ case 'c' : crunch = 0 ; break ;
+ case '0' : delim = "" ; delimlen = 1 ; break ;
+ case 'd' : delim = l.arg ; delimlen = str_len(delim) ; break ;
+ case 'x' :
+ if (!ushort_scanlist(breakcodes, 256, l.arg, &nbc)) dieusage() ;
+ break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ if (argc < 2) dieusage() ;
+ x = argv[0] ; if (!*x) dieusage() ;
+ argv++ ; argc-- ;
+ argc1 = el_semicolon(argv) ;
+ if (!argc1) strerr_dief1x(100, "empty block") ;
+ if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ;
+ argv[argc1] = 0 ;
+ {
+ int fd ;
+ pid_t pidw = el_spawn1(argv[0], argv, envp, &fd, 1) ;
+ if (!pidw) strerr_diefu2sys(111, "spawn ", argv[0]) ;
+ {
+ char buf[BUFFER_INSIZE] ;
+ buffer b = BUFFER_INIT(&buffer_read, fd, buf, BUFFER_INSIZE) ;
+ stralloc modif = STRALLOC_ZERO ;
+ unsigned int envlen = env_len(envp) ;
+ unsigned int modifstart = str_len(x)+1 ;
+ char const *newenv[envlen + 2] ;
+ if (!stralloc_ready(&modif, modifstart+1))
+ strerr_diefu1sys(111, "stralloc_ready") ;
+ byte_copy(modif.s, modifstart-1, x) ;
+ modif.s[modifstart-1] = '=' ;
+ for (;;)
+ {
+ pid_t pid ;
+ modif.len = modifstart ;
+ if (delimlen)
+ {
+ register int r = skagetlnsep(&b, &modif, delim, delimlen) ;
+ if (!r) break ;
+ else if (r < 0)
+ {
+ if (errno != EPIPE) strerr_diefu1sys(111, "skagetlnsep") ;
+ if (chomp) break ;
+ }
+ else modif.len-- ;
+ if ((modif.len == modifstart) && crunch) continue ;
+ }
+ else
+ {
+ unsigned int unread = 0 ;
+ if (netstring_get(&b, &modif, &unread) <= 0)
+ {
+ if (netstring_okeof(&b, unread)) break ;
+ else strerr_diefu1sys(111, "netstring_get") ;
+ }
+ }
+ if (!stralloc_0(&modif)) strerr_diefu1sys(111, "stralloc_0") ;
+ if (!env_merge(newenv, envlen+2, envp, envlen, modif.s, modif.len))
+ strerr_diefu1sys(111, "merge environment") ;
+ pid = el_spawn0(argv[argc1 + 1], argv + argc1 + 1, newenv) ;
+ if (!pid) strerr_diefu2sys(111, "spawn ", argv[argc1+1]) ;
+ if (pids.s)
+ {
+ if (!genalloc_append(pid_t, &pids, &pid))
+ strerr_diefu1sys(111, "genalloc_append") ;
+ }
+ else
+ {
+ int wstat ;
+ if (wait_pid(pid, &wstat) < 0)
+ strerr_diefu2sys(111, "wait for ", argv[argc1 + 1]) ;
+ if (isbreak(breakcodes, nbc, wait_status(wstat)))
+ return wait_status(wstat) ;
+ }
+ }
+ stralloc_free(&modif) ;
+ }
+ fd_close(fd) ;
+ if (!genalloc_append(pid_t, &pids, &pidw))
+ strerr_diefu1sys(111, "genalloc_append") ;
+ }
+ if (!waitn(genalloc_s(pid_t, &pids), genalloc_len(pid_t, &pids)))
+ strerr_diefu1sys(111, "waitn") ;
+ /* genalloc_free(pid_t, &pids) ; */
+ return 0 ;
+}
diff --git a/src/execline/foreground.c b/src/execline/foreground.c
new file mode 100644
index 0000000..8164dcb
--- /dev/null
+++ b/src/execline/foreground.c
@@ -0,0 +1,14 @@
+/* ISC license. */
+
+#include <skalibs/strerr2.h>
+#include <execline/execline.h>
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ int argc1 ;
+ PROG = "foreground" ;
+ argc1 = el_semicolon(++argv) ;
+ if (argc1 >= --argc) strerr_dief1x(100, "unterminated block") ;
+ argv[argc1] = 0 ;
+ el_execsequence(argv, argv+argc1+1, envp) ;
+}
diff --git a/src/execline/forx.c b/src/execline/forx.c
new file mode 100644
index 0000000..25d6d44
--- /dev/null
+++ b/src/execline/forx.c
@@ -0,0 +1,91 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/env.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/skamisc.h>
+#include <skalibs/ushort.h>
+#include <execline/config.h>
+#include <execline/execline.h>
+
+#define USAGE "forx [ -p | -x breakcode,breakcode,... ] var { values... } command..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+static int isbreak (unsigned short *tab, unsigned int n, int code)
+{
+ register unsigned int i = 0 ;
+ for (; i < n ; i++) if ((unsigned short)code == tab[i]) break ;
+ return i < n ;
+}
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ char const *x ;
+ int argc1 ;
+ unsigned short breakcodes[256] ;
+ unsigned int nbc = 0 ;
+ int flagpar = 0 ;
+ PROG = "forx" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "epx:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'e' : break ; /* compat */
+ case 'p' : flagpar = 1 ; break ;
+ case 'x' :
+ if (!ushort_scanlist(breakcodes, 256, l.arg, &nbc)) dieusage() ;
+ break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+
+ if (argc < 2) dieusage() ;
+ x = argv[0] ; if (!*x) dieusage() ;
+ argv++ ; argc-- ;
+ argc1 = el_semicolon(argv) ;
+ if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ;
+ if (!argc1 || (argc1 + 1 == argc)) return 0 ;
+ {
+ unsigned int envlen = env_len(envp) ;
+ unsigned int varlen = str_len(x) ;
+ unsigned int i = 0 ;
+ pid_t pids[flagpar ? argc1 : 1] ;
+ char const *newenv[envlen + 2] ;
+
+ for (; i < (unsigned int)argc1 ; i++)
+ {
+ pid_t pid ;
+ unsigned int vallen = str_len(argv[i]) ;
+ char modif[varlen + vallen + 2] ;
+ byte_copy(modif, varlen, x) ;
+ modif[varlen] = '=' ;
+ byte_copy(modif + varlen + 1, vallen, argv[i]) ;
+ modif[varlen + vallen + 1] = 0 ;
+ if (!env_merge(newenv, envlen + 2, envp, envlen, modif, varlen + vallen + 2))
+ strerr_diefu1sys(111, "build new environment") ;
+ pid = el_spawn0(argv[argc1+1], argv + argc1 + 1, newenv) ;
+ if (!pid) strerr_diefu2sys(111, "spawn ", argv[argc1+1]) ;
+ if (flagpar) pids[i] = pid ;
+ else
+ {
+ int wstat ;
+ if (wait_pid(pid, &wstat) == -1)
+ strerr_diefu2sys(111, "wait for ", argv[argc1+1]) ;
+ if (isbreak(breakcodes, nbc, wait_status(wstat)))
+ return wait_status(wstat) ;
+ }
+ }
+ if (flagpar)
+ if (!waitn(pids, argc1)) strerr_diefu1sys(111, "waitn") ;
+ }
+ return 0 ;
+}
diff --git a/src/execline/getpid.c b/src/execline/getpid.c
new file mode 100644
index 0000000..03a517c
--- /dev/null
+++ b/src/execline/getpid.c
@@ -0,0 +1,29 @@
+/* ISC license. */
+
+#include <unistd.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/env.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "getpid variable prog..."
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ unsigned int len ;
+ PROG = "getpid" ;
+ if (argc < 3) strerr_dieusage(100, USAGE) ;
+ len = str_len(argv[1]) ;
+ if (byte_chr(argv[1], len, '=') < len)
+ strerr_dief2x(100, "invalid variable name: ", argv[1]) ;
+ {
+ char fmt[UINT_FMT + len + 2] ;
+ unsigned int i = len+1 ;
+ byte_copy(fmt, len, argv[1]) ;
+ fmt[len] = '=' ;
+ i += uint_fmt(fmt+i, getpid()) ; fmt[i++] = 0 ;
+ pathexec_r(argv+2, envp, env_len(envp), fmt, i) ;
+ }
+ strerr_dieexec(111, argv[2]) ;
+}
diff --git a/src/execline/heredoc.c b/src/execline/heredoc.c
new file mode 100644
index 0000000..899c2d8
--- /dev/null
+++ b/src/execline/heredoc.c
@@ -0,0 +1,59 @@
+/* ISC license. */
+
+#include <unistd.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/uint.h>
+#include <skalibs/allreadwrite.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "heredoc [ -d ] fd string command..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ int df = 0 ;
+ PROG = "heredoc" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "d", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'd' : df = 1 ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ if (argc < 3) dieusage() ;
+ {
+ int fd[2] ;
+ unsigned int fdr ;
+ int pid ;
+ if (!uint0_scan(argv[0], &fdr)) strerr_dieusage(100, USAGE) ;
+ if (pipe(fd) < 0) strerr_diefu1sys(111, "pipe") ;
+ pid = df ? doublefork() : fork() ;
+ switch (pid)
+ {
+ case -1: strerr_diefu2sys(111, df ? "double" : "", "fork") ;
+ case 0:
+ {
+ unsigned int len = str_len(argv[1]) ;
+ PROG = "heredoc (child)" ;
+ fd_close(fd[0]) ;
+ if (allwrite(fd[1], argv[1], len) < len)
+ strerr_diefu1sys(111, "allwrite") ;
+ return 0 ;
+ }
+ }
+ fd_close(fd[1]) ;
+ if (fd_move((int)fdr, fd[0]) == -1)
+ strerr_diefu2sys(111, "read on fd ", argv[0]) ;
+ }
+ pathexec_run(argv[2], argv+2, envp) ;
+ strerr_dieexec(111, argv[2]) ;
+}
diff --git a/src/execline/homeof.c b/src/execline/homeof.c
new file mode 100644
index 0000000..40463da
--- /dev/null
+++ b/src/execline/homeof.c
@@ -0,0 +1,28 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <errno.h>
+#include <skalibs/buffer.h>
+#include <skalibs/strerr2.h>
+
+#define USAGE "homeof user"
+
+int main (int argc, char const *const *argv)
+{
+ struct passwd *pw ;
+ PROG = "homeof" ;
+ if (argc < 2) strerr_dieusage(100, USAGE) ;
+ pw = getpwnam(argv[1]) ;
+ if (!pw)
+ {
+ if (errno)
+ strerr_diefu2sys(111, "get passwd entry for ", argv[1]) ;
+ else
+ strerr_diefu3x(111, "get passwd entry for ", argv[1], ": no such user") ;
+ }
+ if ((buffer_puts(buffer_1small, pw->pw_dir) < 0)
+ || (buffer_putflush(buffer_1small, "\n", 1) < 0))
+ strerr_diefu1sys(111, "write to stdout") ;
+ return 0 ;
+}
diff --git a/src/execline/if.c b/src/execline/if.c
new file mode 100644
index 0000000..9d0b4b4
--- /dev/null
+++ b/src/execline/if.c
@@ -0,0 +1,54 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/ushort.h>
+#include <execline/execline.h>
+
+#define USAGE "if [ -n ] [ -X ] [ -t | -x exitcode ] { command... }"
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ int argc1, wstat ;
+ pid_t pid ;
+ unsigned int not = 0 ;
+ unsigned short e = 1 ;
+ int flagnormalcrash = 0 ;
+ PROG = "if" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "nXtx:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'n' : not = 1 ; break ;
+ case 'X' : flagnormalcrash = 1 ; break ;
+ case 't' : e = 0 ; break ;
+ case 'x' : if (ushort_scan(l.arg, &e)) break ;
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ argc1 = el_semicolon(argv) ;
+ if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ;
+ argv[argc1] = 0 ;
+ pid = el_spawn0(argv[0], argv, envp) ;
+ if (!pid) strerr_diefu2sys(111, "spawn ", argv[0]) ;
+ if (wait_pid(pid, &wstat) == -1) strerr_diefu1sys(111, "wait_pid") ;
+ if (!flagnormalcrash && WIFSIGNALED(wstat))
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, WTERMSIG(wstat))] = 0 ;
+ strerr_dief2x(1, "child crashed with signal ", fmt) ;
+ }
+ if (not == !wait_status(wstat)) return (int)e ;
+ pathexec0_run(argv+argc1+1, envp) ;
+ strerr_dieexec(111, argv[argc1+1]) ;
+}
diff --git a/src/execline/ifelse.c b/src/execline/ifelse.c
new file mode 100644
index 0000000..6d8801e
--- /dev/null
+++ b/src/execline/ifelse.c
@@ -0,0 +1,54 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/uint.h>
+#include <execline/execline.h>
+
+#define USAGE "ifelse [ -n ] [ -X ] { command-if } { command-then... }"
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ int argc1, argc2, wstat ;
+ int not = 0, flagnormalcrash = 0 ;
+ pid_t pid ;
+ PROG = "ifelse" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "nX", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'n' : not = 1 ; break ;
+ case 'X' : flagnormalcrash = 1 ; break ;
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ argc1 = el_semicolon(argv) ;
+ if (argc1 >= argc) strerr_dief1x(100, "unterminated if block") ;
+ if (argc1 + 1 == argc) strerr_dief1x(100, "then block required") ;
+ argc2 = el_semicolon(argv + argc1 + 1) ;
+ if (argc1 + argc2 + 1 >= argc) strerr_dief1x(100, "unterminated then block") ;
+ argv[argc1] = 0 ;
+ pid = el_spawn0(argv[0], argv, envp) ;
+ if (!pid) strerr_diefu2sys(111, "spawn ", argv[0]) ;
+ if (wait_pid(pid, &wstat) == -1)
+ strerr_diefu2sys(111, "wait for ", argv[0]) ;
+ argv += ++argc1 ;
+ if (!flagnormalcrash && WIFSIGNALED(wstat))
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, WSTOPSIG(wstat))] = 0 ;
+ strerr_dief2x(1, "child crashed with signal ", fmt) ;
+ }
+ if (not != !wait_status(wstat)) argv[argc2] = 0 ; else argv += argc2+1 ;
+ pathexec0_run(argv, envp) ;
+ strerr_dieexec(111, *argv) ;
+}
diff --git a/src/execline/ifte.c b/src/execline/ifte.c
new file mode 100644
index 0000000..3fe021e
--- /dev/null
+++ b/src/execline/ifte.c
@@ -0,0 +1,59 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+#include <execline/execline.h>
+
+#define USAGE "ifte [ -X ] [ -n ] { command-then... } { command-else... } command-if..."
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ int argc1, argc2, wstat ;
+ int not = 0, flagnormalcrash = 0 ;
+ pid_t pid ;
+ PROG = "ifte" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "Xn", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'X' : flagnormalcrash = 1 ; break ;
+ case 'n' : not = 1 ; break ;
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ argc1 = el_semicolon(argv) ;
+ if (argc1 >= argc) strerr_dief1x(100, "unterminated then block") ;
+ if (argc1 + 1 == argc) strerr_dief1x(100, "else block required") ;
+ argc2 = el_semicolon(argv + argc1 + 1) ;
+ if (argc1 + argc2 + 1 >= argc) strerr_dief1x(100, "unterminated else block") ;
+ if (argc1 + argc2 + 2 >= argc) strerr_dief1x(100, "empty command-if") ;
+
+ pid = el_spawn0(argv[argc1 + argc2 + 2], argv + argc1 + argc2 + 2, envp) ;
+ if (!pid) strerr_diefu2sys(111, "spawn ", argv[argc1 + argc2 + 2]) ;
+ if (wait_pid(pid, &wstat) == -1)
+ strerr_diefu2sys(111, "wait for ", argv[argc1 + argc2 + 2]) ;
+ if (!flagnormalcrash && WIFSIGNALED(wstat))
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, WTERMSIG(wstat))] = 0 ;
+ strerr_dief2x(1, "child crashed with signal ", fmt) ;
+ }
+ if (not != !wait_status(wstat)) argv[argc1] = 0 ;
+ else
+ {
+ argv += argc1 + 1 ;
+ argv[argc2] = 0 ;
+ }
+ pathexec0_run(argv, envp) ;
+ strerr_dieexec(111, *argv) ;
+}
diff --git a/src/execline/ifthenelse.c b/src/execline/ifthenelse.c
new file mode 100644
index 0000000..f9fb9d7
--- /dev/null
+++ b/src/execline/ifthenelse.c
@@ -0,0 +1,77 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+#include <execline/execline.h>
+
+#define USAGE "ifthenelse [ -X ] { command-if... } { command-then... } { command-else... } [ remainder... ]"
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ int argc1, argc2, argc3, wstat ;
+ int magicscope = 0, flagnormalcrash = 0 ;
+ pid_t pid ;
+ PROG = "ifthenelse" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "Xs", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'X' : flagnormalcrash = 1 ; break ;
+ case 's' : magicscope = 1 ; break ;
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ argc1 = el_semicolon(argv) ;
+ if (argc1 >= argc) strerr_dief1x(100, "unterminated if block") ;
+ if (argc1 + 1 == argc) strerr_dief1x(100, "then block required") ;
+ argc2 = el_semicolon(argv + argc1 + 1) ;
+ if (argc1 + argc2 + 1 >= argc) strerr_dief1x(100, "unterminated then block") ;
+ argc3 = el_semicolon(argv + argc1 + argc2 + 2) ;
+ if (argc1 + argc2 + argc3 + 2 >= argc)
+ strerr_dief1x(100, "unterminated else block") ;
+
+ argv[argc1] = 0 ;
+ pid = fork() ;
+ pid = el_spawn0(argv[0], argv, envp) ;
+ if (!pid) strerr_diefu2sys(111, "spawn ", argv[0]) ;
+ if (wait_pid(pid, &wstat) == -1)
+ strerr_diefu2sys(111, "wait for ", argv[0]) ;
+ argv += argc1 + 1 ;
+ {
+ char const *const *remainder = argv + argc2 + argc3 + 2 ;
+ if (!flagnormalcrash && WIFSIGNALED(wstat))
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, WTERMSIG(wstat))] = 0 ;
+ strerr_dief2x(1, "child crashed with signal ", fmt) ;
+ }
+ if (wait_status(wstat))
+ {
+ argv += argc2 + 1 ;
+ argc2 = argc3 ;
+ }
+ if (magicscope) /* undocumented voodoo - dangerous and powerful */
+ {
+ register unsigned int i = 0 ;
+ for (; remainder[i] ; i++) argv[argc2+i] = remainder[i] ;
+ argv[argc2+i] = 0 ;
+ pathexec0_run(argv, envp) ;
+ strerr_dieexec(111, argv[0]) ;
+ }
+ else
+ {
+ argv[argc2] = 0 ;
+ el_execsequence(argv, remainder, envp) ;
+ }
+ }
+}
diff --git a/src/execline/import.c b/src/execline/import.c
new file mode 100644
index 0000000..5a95886
--- /dev/null
+++ b/src/execline/import.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/strerr2.h>
+#include "exlsn.h"
+
+#define USAGE "import [ -i | -D default ] [ -n ] [ -s ] [ -C | -c ] [ -d delim ] var prog..."
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ PROG = "import" ;
+ exlsn_main(argc, argv, envp, &exlsn_import, USAGE) ;
+}
diff --git a/src/execline/importas.c b/src/execline/importas.c
new file mode 100644
index 0000000..026efce
--- /dev/null
+++ b/src/execline/importas.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/strerr2.h>
+#include "exlsn.h"
+
+#define USAGE "importas [ -i | -D default ] [ -n ] [ -s ] [ -C | -c ] [ -d delim ] key var prog..."
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ PROG = "importas" ;
+ exlsn_main(argc, argv, envp, &exlsn_importas, USAGE) ;
+}
diff --git a/src/execline/loopwhilex.c b/src/execline/loopwhilex.c
new file mode 100644
index 0000000..5cb6a4e
--- /dev/null
+++ b/src/execline/loopwhilex.c
@@ -0,0 +1,62 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/ushort.h>
+#include <skalibs/djbunix.h>
+#include <execline/execline.h>
+
+#define USAGE "loopwhilex [ -n ] [ -x exitcode,exitcode,... ] prog..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+static int isbreak (unsigned short *tab, unsigned int n, int code)
+{
+ register unsigned int i = 0 ;
+ for (; i < n ; i++) if ((unsigned short)code == tab[i]) break ;
+ return i < n ;
+}
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ int wstat ;
+ int not = 0, cont = 1 ;
+ unsigned short breakcodes[256] ;
+ unsigned int nbc = 0 ;
+ PROG = "loopwhilex" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "nx:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'n' : not = 1 ; break ;
+ case 'x' :
+ if (!ushort_scanlist(breakcodes, 256, l.arg, &nbc)) dieusage() ;
+ break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ if (!argc) dieusage() ;
+
+ if (!nbc)
+ {
+ breakcodes[0] = 0 ;
+ nbc = 1 ;
+ not = !not ;
+ }
+
+ while (cont)
+ {
+ pid_t pid = el_spawn0(argv[0], argv, envp) ;
+ if (!pid) strerr_diefu2sys(111, "spawn ", argv[0]) ;
+ if (wait_pid(pid, &wstat) < 0) strerr_diefu1sys(111, "wait_pid") ;
+ cont = not == isbreak(breakcodes, nbc, wait_status(wstat)) ;
+ }
+ return WIFSIGNALED(wstat) ? WTERMSIG(wstat) : 0 ;
+}
diff --git a/src/execline/multidefine.c b/src/execline/multidefine.c
new file mode 100644
index 0000000..9665418
--- /dev/null
+++ b/src/execline/multidefine.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/strerr2.h>
+#include "exlsn.h"
+
+#define USAGE "multidefine [ -0 ] [ -r ] [ -n ] [ -C | -c ] [ -d delim ] value { vars... } prog..."
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ PROG = "multidefine" ;
+ exlsn_main(argc, argv, envp, &exlsn_multidefine, USAGE) ;
+}
diff --git a/src/execline/multisubstitute.c b/src/execline/multisubstitute.c
new file mode 100644
index 0000000..acee42f
--- /dev/null
+++ b/src/execline/multisubstitute.c
@@ -0,0 +1,64 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/strerr2.h>
+#include <execline/execline.h>
+#include "exlsn.h"
+
+#define USAGE "see http://skarnet.org/software/execline/multisubstitute.html"
+
+static char const *const commands[8] =
+{
+ "define",
+ "importas",
+ "import",
+ "elglob",
+ "elgetpositionals",
+ "multidefine",
+ 0
+} ;
+
+static exlsnfunc_t *const functions[8] =
+{
+ &exlsn_define,
+ &exlsn_importas,
+ &exlsn_import,
+ &exlsn_elglob,
+ &exlsn_exlp,
+ &exlsn_multidefine,
+ 0
+} ;
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ exlsn_t info = EXLSN_ZERO ;
+ int argc1 ;
+ PROG = "multisubstitute" ;
+ if (!--argc) strerr_dieusage(100, USAGE) ;
+
+ /* Read a block containing directives */
+ argc1 = el_semicolon(++argv) ;
+ if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ;
+ if (argc1 + 1 == argc) strerr_dieusage(100, USAGE) ;
+
+ /* Parse args and update the substitution info */
+ while (argc1)
+ {
+ int n ;
+ unsigned int i = 0 ;
+ for (; commands[i] ; i++) if (!str_diff(*argv, commands[i])) break ;
+ if (!commands[i]) strerr_dief3x(100, "syntax error: unrecognized", " directive ", *argv) ;
+ n = (*(functions[i]))(argc1, argv, envp, &info) ;
+ if (n < 0) switch (n)
+ {
+ case -3 : strerr_dief3x(100, "syntax error at", " directive ", commands[i]) ;
+ case -2 : strerr_dief3x(100, "wrong key for", " directive ", commands[i]) ;
+ case -1 : strerr_diefu3sys(111, "run", " directive ", commands[i]) ;
+ default : strerr_dief3x(111, "unknown error with", " directive ", commands[i]) ;
+ }
+ argv += n ; argc1 -= n ; argc -= n ;
+ }
+
+ /* Perform the substitution and exec */
+ el_substandrun(argc-1, argv+1, envp, &info) ;
+}
diff --git a/src/execline/pipeline.c b/src/execline/pipeline.c
new file mode 100644
index 0000000..42a230b
--- /dev/null
+++ b/src/execline/pipeline.c
@@ -0,0 +1,85 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <unistd.h>
+#ifdef EXECLINE_OLD_VARNAMES
+#include <skalibs/bytestr.h>
+#endif
+#include <skalibs/sgetopt.h>
+#include <skalibs/uint64.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/env.h>
+#include <skalibs/djbunix.h>
+#include <execline/execline.h>
+
+#define USAGE "pipeline [ -d ] [ -r | -w ] { command... } command..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ int df = 0, w = 0 ;
+ PROG = "pipeline" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "drw", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'd' : df = 1 ; break ;
+ case 'r' : w = 0 ; break ;
+ case 'w' : w = 1 ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ {
+ pid_t pid ;
+ int fd ;
+ int argc1 = el_semicolon(argv) ;
+ if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ;
+ if (argc1 + 1 == argc) strerr_dief1x(100, "empty remainder") ;
+ argv[argc1] = 0 ;
+ if (df)
+ {
+ int p[2] ;
+ if (pipe(p) < 0) strerr_diefu1sys(111, "create pipe") ;
+ pid = doublefork() ;
+ switch (pid)
+ {
+ case -1: strerr_diefu1sys(111, "doublefork") ;
+ case 0:
+ PROG = "pipeline (grandchild)" ;
+ fd_close(p[w]) ;
+ if (fd_move(!w, p[!w]) < 0) strerr_diefu1sys(111, "fd_move") ;
+ pathexec0_run(argv, envp) ;
+ strerr_dieexec(127, argv[0]) ;
+ }
+ fd_close(p[!w]) ;
+ fd = p[w] ;
+ }
+ else
+ {
+ pid = el_spawn1(argv[0], argv, envp, &fd, !w) ;
+ if (!pid) strerr_diefu2sys(111, "spawn ", argv[0]) ;
+ }
+ if (fd_move(w, fd) < 0) strerr_diefu1sys(111, "fd_move") ;
+ {
+#ifdef EXECLINE_OLD_VARNAMES
+ char fmt[UINT64_FMT * 2 + 10] = "!=" ;
+#else
+ char fmt[UINT64_FMT + 2] = "!=" ;
+#endif
+ register unsigned int i = 2 ;
+ i += uint64_fmt(fmt+i, (uint64)pid) ; fmt[i++] = 0 ;
+#ifdef EXECLINE_OLD_VARNAMES
+ byte_copy(fmt+i, 8, "LASTPID=") ; i += 8 ;
+ i += uint64_fmt(fmt+i, (uint64)pid) ; fmt[i++] = 0 ;
+#endif
+ pathexec_r(argv + argc1 + 1, envp, env_len(envp), fmt, i) ;
+ }
+ strerr_dieexec(111, argv[argc1 + 1]) ;
+ }
+}
diff --git a/src/execline/piperw.c b/src/execline/piperw.c
new file mode 100644
index 0000000..9d24947
--- /dev/null
+++ b/src/execline/piperw.c
@@ -0,0 +1,29 @@
+/* ISC license. */
+
+#include <unistd.h>
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "piperw fdr fdw prog..."
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ int fdr, fdw ;
+ int p[2] ;
+ PROG = "piperw" ;
+ if ((argc < 4)
+ || !uint0_scan(argv[1], (unsigned int *)&fdr)
+ || !uint0_scan(argv[2], (unsigned int *)&fdw)
+ || (fdr == fdw))
+ strerr_dieusage(100, USAGE) ;
+ if (pipe(p) == -1)
+ strerr_diefu1sys(111, "create pipe") ;
+ if (p[1] == fdr) p[1] = dup(p[1]) ;
+ if ((p[1] == -1)
+ || (fd_move(fdr, p[0]) == -1)
+ || (fd_move(fdw, p[1]) == -1))
+ strerr_diefu1sys(111, "move fds") ;
+ pathexec_run(argv[3], argv+3, envp) ;
+ strerr_dieexec(111, argv[3]) ;
+}
diff --git a/src/execline/redirfd.c b/src/execline/redirfd.c
new file mode 100644
index 0000000..d9dca14
--- /dev/null
+++ b/src/execline/redirfd.c
@@ -0,0 +1,69 @@
+/* ISC license. */
+
+#include <fcntl.h>
+#include <errno.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "redirfd -[ r | w | u | a | c | x ] [ -n ] [ -b ] fd file prog..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ int fd, fd2 ;
+ unsigned int flags = 0 ;
+ int what = -1 ;
+ int changemode = 0 ;
+ PROG = "redirfd" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "rwuacxnb", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'r' : what = O_RDONLY ; flags &= ~(O_APPEND|O_CREAT|O_TRUNC|O_EXCL) ; break ;
+ case 'w' : what = O_WRONLY ; flags |= O_CREAT|O_TRUNC ; flags &= ~(O_APPEND|O_EXCL) ; break ;
+ case 'u' : what = O_RDWR ; flags &= ~(O_APPEND|O_CREAT|O_TRUNC|O_EXCL) ; break ;
+ case 'a' : what = O_WRONLY ; flags |= O_CREAT|O_APPEND ; flags &= ~(O_TRUNC|O_EXCL) ; break ;
+ case 'c' : what = O_WRONLY ; flags |= O_APPEND ; flags &= ~(O_CREAT|O_TRUNC|O_EXCL) ; break ;
+ case 'x' : what = O_WRONLY ; flags |= O_CREAT|O_EXCL ; flags &= ~(O_APPEND|O_TRUNC) ; break ;
+ case 'n' : flags |= O_NONBLOCK ; break ;
+ case 'b' : changemode = 1 ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ if ((argc < 3) || (what == -1)) dieusage() ;
+ if (!uint0_scan(argv[0], (unsigned int *)&fd)) dieusage() ;
+ flags |= what ;
+ fd2 = open3(argv[1], flags, 0666) ;
+ if ((fd2 == -1) && (what == O_WRONLY) && (errno == ENXIO))
+ {
+ register int e ;
+ int fdr = open_read(argv[1]) ;
+ if (fdr == -1) strerr_diefu2sys(111, "open_read ", argv[1]) ;
+ fd2 = open3(argv[1], flags, 0666) ;
+ e = errno ;
+ fd_close(fdr) ;
+ errno = e ;
+ }
+ if (fd2 == -1) strerr_diefu2sys(111, "open ", argv[1]) ;
+ if (fd_move(fd, fd2) == -1)
+ {
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, fd2)] = 0 ;
+ strerr_diefu4sys(111, "move fd ", fmt, " to fd ", argv[0]) ;
+ }
+ if (changemode)
+ {
+ if (((flags & O_NONBLOCK) ? ndelay_off(fd) : ndelay_on(fd)) < 0)
+ strerr_diefu1sys(111, "change blocking mode") ;
+ }
+ pathexec_run(argv[2], argv+2, envp) ;
+ strerr_dieexec(111, argv[2]) ;
+}
diff --git a/src/execline/runblock.c b/src/execline/runblock.c
new file mode 100644
index 0000000..a654023
--- /dev/null
+++ b/src/execline/runblock.c
@@ -0,0 +1,149 @@
+/* ISC license. */
+
+#include <skalibs/sgetopt.h>
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/env.h>
+#include <skalibs/djbunix.h>
+#include <skalibs/skamisc.h>
+#include <execline/execline.h>
+
+#define USAGE "runblock [ -P ] [ -n argshift ] [ -r ] n"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ genalloc v = GENALLOC_ZERO ; /* array of char const * */
+ unsigned int n, sharp ;
+ unsigned int m = 0 ;
+ unsigned int strict = el_getstrict() ;
+ int flagnopop = 0, flagr = 0 ;
+ PROG = "runblock" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "Prn:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'P' : flagnopop = 1 ; break ;
+ case 'r' : flagr = 1 ; break ;
+ case 'n' : if (!uint0_scan(l.arg, &m)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ if (argc != 1) dieusage() ;
+ if (!uint0_scan(argv[0], &n) || (!n && !flagr)) dieusage() ;
+
+ {
+ char const *x = env_get2(envp, "#") ;
+ if (!x) strerr_dienotset(100, "#") ;
+ if (!uint0_scan(x, &sharp)) strerr_dieinvalid(100, "#") ;
+ }
+
+ /* Skip n-1 blocks (n if flagr) */
+ {
+ register unsigned int i = 1 ;
+ for (; i < n + flagr ; i++)
+ {
+ unsigned int base = m ;
+ for (;;)
+ {
+ char const *x ;
+ char fmt[UINT_FMT] ;
+ if (++m > sharp) strerr_dief1x(100, "too few arguments") ;
+ fmt[uint_fmt(fmt, m)] = 0 ;
+ x = env_get2(envp, fmt) ;
+ if (!x) strerr_dienotset(100, fmt) ;
+ if ((x[0] == EXECLINE_BLOCK_END_CHAR) && (!EXECLINE_BLOCK_END_CHAR || !x[1])) break ;
+ if ((x[0] != EXECLINE_BLOCK_QUOTE_CHAR) && strict)
+ {
+ char fmtb[UINT_FMT] ;
+ char fmti[UINT_FMT] ;
+ fmti[uint_fmt(fmti, i)] = 0 ;
+ fmtb[uint_fmt(fmtb, m - base)] = 0 ;
+ if (strict == 1)
+ strerr_warnw6x("unquoted positional ", x, " at block ", fmti, " position ", fmtb) ;
+ else
+ strerr_dief6x(100, "unquoted positional ", x, " at block ", fmti, " position ", fmtb) ;
+ }
+ }
+ }
+ }
+
+ if (flagr) /* put remainder envvars into v */
+ {
+ if (++m == sharp) return 0 ;
+ if (!genalloc_ready(char const *, &v, sharp - m + 2))
+ strerr_diefu1sys(111, "genalloc_ready") ;
+ for (; m <= sharp ; m++)
+ {
+ char const *x ;
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, m)] = 0 ;
+ x = env_get2(envp, fmt) ;
+ if (!x) strerr_dienotset(100, fmt) ;
+ genalloc_append(char const *, &v, &x) ;
+ }
+ }
+ else /* put envvars from nth block into v */
+ {
+ unsigned int base = m ;
+ for (;;)
+ {
+ char const *x ;
+ char fmt[UINT_FMT] ;
+ if (++m > sharp) strerr_dief1x(100, "too few arguments") ;
+ fmt[uint_fmt(fmt, m)] = 0 ;
+ x = env_get2(envp, fmt) ;
+ if (!x) strerr_dienotset(100, fmt) ;
+ if ((x[0] == EXECLINE_BLOCK_END_CHAR) && (!EXECLINE_BLOCK_END_CHAR || !x[1])) break ;
+ if (x[0] != EXECLINE_BLOCK_QUOTE_CHAR)
+ {
+ if (strict)
+ {
+ char fmtb[UINT_FMT] ;
+ char fmtn[UINT_FMT] ;
+ fmtn[uint_fmt(fmtn, n)] = 0 ;
+ fmtb[uint_fmt(fmtb, m - base)] = 0 ;
+ if (strict == 1)
+ strerr_warnw6x("unquoted positional ", x, " at block ", fmtn, " position ", fmtb) ;
+ else
+ strerr_dief6x(100, "unquoted positional ", x, " at block ", fmtn, " position ", fmtb) ;
+ }
+ }
+ else x++ ;
+ if (!genalloc_append(char const *, &v, &x)) strerr_diefu1sys(111, "envalloc_catb") ;
+ }
+ }
+
+ {
+ char const *z = 0 ;
+ if (!genalloc_append(char const *, &v, &z))
+ strerr_diefu1sys(111, "genalloc_append") ;
+ }
+
+ if (flagnopop) /* exec now */
+ pathexec_run(genalloc_s(char const *, &v)[0], genalloc_s(char const *, &v), envp) ;
+ else /* popenv, then exec */
+ {
+ char const *list[11] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "#" } ;
+ unsigned int envlen = env_len(envp) ;
+ register int popped = el_popenv(&satmp, envp, envlen, list, 11) ;
+ if (popped < 0) strerr_diefu1sys(111, "pop environment") ;
+ else
+ {
+ char const *w[envlen - popped + 1] ;
+ if (!env_make(w, envlen - popped, satmp.s, satmp.len))
+ strerr_diefu1sys(111, "env_make") ;
+ w[envlen - popped] = 0 ;
+ pathexec_run(genalloc_s(char const *, &v)[0], genalloc_s(char const *, &v), w) ;
+ stralloc_free(&satmp) ;
+ }
+ }
+ strerr_dieexec(111, genalloc_s(char const *, &v)[0]) ;
+}
diff --git a/src/execline/shift.c b/src/execline/shift.c
new file mode 100644
index 0000000..b09ba38
--- /dev/null
+++ b/src/execline/shift.c
@@ -0,0 +1,121 @@
+/* ISC license. */
+
+#include <skalibs/sgetopt.h>
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+#include <execline/execline.h>
+
+#define USAGE "shift [ -n arg# ] [ -b block# ] prog..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ unsigned int strict = el_getstrict() ;
+ unsigned int b = 0 ;
+ unsigned int n = 0 ;
+ unsigned int sharp ;
+ unsigned int i = 0 ;
+ PROG = "shift" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "n:b:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'n' :
+ if (!uint0_scan(l.arg, &n)) dieusage() ;
+ i = 1 ;
+ break ;
+ case 'b' :
+ if (!uint0_scan(l.arg, &b)) dieusage() ;
+ i = 1 ;
+ break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ if (!argc) dieusage() ;
+ if (i) i = 0 ; else n = 1 ;
+ {
+ char const *x = env_get2(envp, "#") ;
+ if (!x) strerr_dienotset(100, "#") ;
+ if (!uint0_scan(x, &sharp)) strerr_dieinvalid(100, "#") ;
+ }
+
+
+ /* Shift n args */
+
+ if (n > sharp)
+ {
+ if (strict)
+ {
+ char fmtn[UINT_FMT] ;
+ char fmtsharp[UINT_FMT] ;
+ fmtn[uint_fmt(fmtn, n)] = 0 ;
+ fmtsharp[uint_fmt(fmtsharp, sharp)] = 0 ;
+ if (strict == 1)
+ strerr_warnwu5x("shift", " ", fmtn, " arguments: got ", fmtsharp) ;
+ else
+ strerr_diefu5x(100, "shift", " ", fmtn, " arguments: got ", fmtsharp) ;
+ }
+ n = sharp ;
+ }
+
+
+ /* Shift b blocks */
+
+ for (; i < b ; i++)
+ {
+ for (;;)
+ {
+ char const *x ;
+ unsigned int base = n ;
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, ++n)] = 0 ;
+ if (n > sharp)
+ {
+ char fmti[UINT_FMT] ;
+ fmti[uint_fmt(fmt, i)] = 0 ;
+ strerr_diefu6x(100, "shift", " block ", fmti, ": too few arguments (", fmt, ")") ;
+ }
+ x = env_get2(envp, fmt) ;
+ if (!x) strerr_dienotset(100, fmt) ;
+ if ((x[0] == EXECLINE_BLOCK_END_CHAR) && (!EXECLINE_BLOCK_END_CHAR || !x[1])) break ;
+ if ((x[0] != EXECLINE_BLOCK_QUOTE_CHAR) && strict)
+ {
+ char fmti[UINT_FMT] ;
+ char fmtp[UINT_FMT] ;
+ fmti[uint_fmt(fmti, i)] = 0 ;
+ fmtp[uint_fmt(fmtp, n - base)] = 0 ;
+ if (strict == 1)
+ strerr_warnw6x("unquoted positional ", x, " at block ", fmti, " position ", fmtp) ;
+ else
+ strerr_dief6x(100, "unquoted positional ", x, " at block ", fmti, " position ", fmtp) ;
+ }
+ }
+ }
+
+
+ /* n = shift value; modify the env */
+
+ {
+ register unsigned int i = 1 ;
+ char fmt[UINT_FMT] ;
+ fmt[uint_fmt(fmt, sharp - n)] = 0 ;
+ if (!pathexec_env("#", fmt)) strerr_diefu1sys(111, "pathexec_env") ;
+ for (; i <= sharp ; i++)
+ {
+ char fmu[UINT_FMT] ;
+ fmt[uint_fmt(fmt, i)] = 0 ;
+ fmu[uint_fmt(fmu, i + n)] = 0 ;
+ if (!pathexec_env(fmt, i <= (sharp - n) ? env_get2(envp, fmu) : 0))
+ strerr_diefu1sys(111, "pathexec_env") ;
+ }
+ }
+ pathexec(argv) ;
+ strerr_dieexec(111, argv[0]) ;
+}
diff --git a/src/execline/tryexec.c b/src/execline/tryexec.c
new file mode 100644
index 0000000..46d3479
--- /dev/null
+++ b/src/execline/tryexec.c
@@ -0,0 +1,61 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+#include <execline/execline.h>
+
+#define USAGE "tryexec [ -n ] [ -c ] [ -l ] [ -a argv0 ] { command... }"
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ char const *env_zero[1] = { 0 } ;
+ char const *executable = 0 ;
+ char const *argv0 = 0 ;
+ char const **dom = 0 ;
+ char const **sub = 0 ;
+ char const *const *dom_envp = envp ;
+ int not = 0, dash = 0 ;
+ PROG = "tryexec" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "ncla:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'n' : not = 1 ; break ;
+ case 'c' : dom_envp = env_zero ; break ;
+ case 'l' : dash = 1 ; break ;
+ case 'a' : argv0 = l.arg ; break ;
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ {
+ int argc1 = el_semicolon(argv) ;
+ if (!argc1) strerr_dief1x(100, "empty block") ;
+ if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ;
+ argv[argc1] = 0 ;
+ dom = argv + not * (argc1 + 1) ;
+ sub = argv + !not * (argc1 + 1) ;
+ }
+ executable = dom[0] ;
+ if (argv0) dom[0] = argv0 ;
+ if (dash)
+ {
+ register unsigned int n = str_len(dom[0]) ;
+ char dashed[n+2] ;
+ dashed[0] = '-' ;
+ byte_copy(dashed+1, n+1, dom[0]) ;
+ dom[0] = dashed ;
+ pathexec_run(executable, dom, dom_envp) ;
+ }
+ else pathexec_run(executable, dom, dom_envp) ;
+
+ pathexec0_run(sub, envp) ;
+ strerr_dieexec(111, sub[0]) ;
+}
diff --git a/src/execline/umask.c b/src/execline/umask.c
new file mode 100644
index 0000000..81217b6
--- /dev/null
+++ b/src/execline/umask.c
@@ -0,0 +1,20 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "umask value prog..."
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ unsigned int m ;
+ PROG = "umask" ;
+ if (argc < 3) strerr_dieusage(100, USAGE) ;
+ if (!uint_oscan(argv[1], &m)) strerr_dieusage(100, USAGE) ;
+ umask(m) ;
+ pathexec_run(argv[2], argv+2, envp) ;
+ strerr_dieexec(111, argv[2]) ;
+}
diff --git a/src/execline/unexport.c b/src/execline/unexport.c
new file mode 100644
index 0000000..189249c
--- /dev/null
+++ b/src/execline/unexport.c
@@ -0,0 +1,20 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/env.h>
+#include <skalibs/djbunix.h>
+
+#define USAGE "unexport variable prog..."
+
+int main (int argc, char const *const *argv, char const *const *envp)
+{
+ unsigned int len ;
+ PROG = "unexport" ;
+ if (argc < 3) strerr_dieusage(100, USAGE) ;
+ len = str_len(argv[1]) ;
+ if (byte_chr(argv[1], len, '=') < len)
+ strerr_dief2x(100, "invalid variable name: ", argv[1]) ;
+ pathexec_r(argv+2, envp, env_len(envp), argv[1], len+1) ;
+ strerr_dieexec(111, argv[2]) ;
+}
diff --git a/src/execline/wait.c b/src/execline/wait.c
new file mode 100644
index 0000000..4d550c9
--- /dev/null
+++ b/src/execline/wait.c
@@ -0,0 +1,62 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/uint.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/djbunix.h>
+#include <execline/execline.h>
+
+#define USAGE "wait [ -r ] { pids... }"
+
+static unsigned int waitall (void)
+{
+ register unsigned int n = 0 ;
+ int wstat ;
+ while (wait(&wstat) > 0) n++ ;
+ return n ;
+}
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+ int argc1 ;
+ int flagreap = 0 ;
+ PROG = "wait" ;
+ {
+ subgetopt_t l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "r", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'r' : flagreap = 1 ; break ;
+ default : strerr_dieusage(100, USAGE) ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+ argc1 = el_semicolon(argv) ;
+ if (argc1 >= argc) strerr_dief1x(100, "unterminated block") ;
+ if (!argc1) flagreap ? wait_reap() : waitall() ;
+ else
+ {
+ pid_t tab[argc1] ;
+ register unsigned int i = 0 ;
+ for (; i < (unsigned int)argc1 ; i++)
+ {
+ unsigned int pid ;
+ if (!uint0_scan(argv[i], &pid)) strerr_dieusage(100, USAGE) ;
+ tab[i] = pid ;
+ }
+ if (flagreap)
+ {
+ if (waitn_reap(tab, argc1) < 0)
+ strerr_diefu1sys(111, "waitn_reap") ;
+ }
+ else if (!waitn(tab, argc1)) strerr_diefu1sys(111, "waitn") ;
+ }
+ pathexec0_run(argv + argc1 + 1, envp) ;
+ strerr_dieexec(111, argv[argc1 + 1]) ;
+}
diff --git a/src/include-local/exlsn.h b/src/include-local/exlsn.h
new file mode 100644
index 0000000..3665bca
--- /dev/null
+++ b/src/include-local/exlsn.h
@@ -0,0 +1,36 @@
+/* ISC license. */
+
+#ifndef EXLSN_H
+#define EXLSN_H
+
+#include <skalibs/gccattributes.h>
+#include <skalibs/stralloc.h>
+
+typedef struct exlsn_s exlsn_t, *exlsn_t_ref ;
+struct exlsn_s
+{
+ stralloc vars ;
+ stralloc values ;
+ stralloc data ; /* array of elsubst */
+} ;
+
+#define EXLSN_ZERO { .vars = STRALLOC_ZERO, .values = STRALLOC_ZERO, .data = STRALLOC_ZERO }
+
+extern void exlsn_free (exlsn_t *) ;
+
+typedef int exlsnfunc_t (int, char const **, char const *const *, exlsn_t *) ;
+typedef exlsnfunc_t *exlsnfunc_t_ref ;
+
+extern exlsnfunc_t exlsn_define ;
+extern exlsnfunc_t exlsn_importas ;
+extern exlsnfunc_t exlsn_import ;
+extern exlsnfunc_t exlsn_elglob ;
+extern exlsnfunc_t exlsn_exlp ;
+extern exlsnfunc_t exlsn_multidefine ;
+
+extern int exlp (unsigned int, char const *const *, exlsn_t *) ;
+extern void el_substandrun (int, char const *const *, char const *const *, exlsn_t *) gccattr_noreturn ;
+extern void el_substandrun_str (stralloc *, unsigned int, char const *const *, exlsn_t *) gccattr_noreturn ;
+extern void exlsn_main (int, char const **, char const *const *, exlsnfunc_t *, char const *) gccattr_noreturn ;
+
+#endif
diff --git a/src/include/execline/execline.h b/src/include/execline/execline.h
new file mode 100644
index 0000000..a5c16ad
--- /dev/null
+++ b/src/include/execline/execline.h
@@ -0,0 +1,67 @@
+/* ISC license. */
+
+#ifndef EXECLINE_H
+#define EXECLINE_H
+
+#include <sys/types.h>
+#include <skalibs/gccattributes.h>
+#include <skalibs/stralloc.h>
+
+#define EXECLINE_BLOCK_QUOTE_CHAR ' '
+#define EXECLINE_BLOCK_END_CHAR '\0'
+
+
+/* Basics */
+
+extern int el_vardupl (char const *, char const *, unsigned int) gccattr_pure ;
+extern unsigned int el_getstrict (void) gccattr_const ;
+extern void el_obsolescent (void) ;
+
+
+/* Environment shifting */
+
+extern int el_pushenv (stralloc *, char const *const *, unsigned int, char const *const *, unsigned int) ;
+extern int el_popenv (stralloc *, char const *const *, unsigned int, char const *const *, unsigned int) ;
+
+
+/* Sequence */
+
+extern pid_t el_spawn0 (char const *, char const *const *, char const *const *) ;
+extern pid_t el_spawn1 (char const *, char const *const *, char const *const *, int *, int) ;
+extern void el_execsequence (char const *const *, char const *const *, char const *const *) gccattr_noreturn ;
+
+
+/* Block unquoting */
+
+extern int el_semicolon (char const **) ;
+
+
+/* Value transformation */
+
+typedef struct eltransforminfo_s eltransforminfo_t, *eltransforminfo_t_ref ;
+struct eltransforminfo_s
+{
+ char const *delim ;
+ unsigned int crunch : 1 ;
+ unsigned int chomp : 1 ;
+ unsigned int split : 1 ;
+} ;
+
+#define ELTRANSFORMINFO_ZERO { .delim = " \n\r\t", .crunch = 0, .chomp = 0, .split = 0 }
+
+extern int el_transform (stralloc *, unsigned int, eltransforminfo_t const *) ;
+
+
+/* Substitution */
+
+typedef struct elsubst_s elsubst_t, *elsubst_t_ref ;
+struct elsubst_s
+{
+ unsigned int var ;
+ unsigned int value ;
+ unsigned int n ;
+} ;
+
+extern int el_substitute (stralloc *, char const *, unsigned int, char const *, char const *, elsubst_t const *, unsigned int) ;
+
+#endif
diff --git a/src/libexecline/deps-lib/execline b/src/libexecline/deps-lib/execline
new file mode 100755
index 0000000..69d483d
--- /dev/null
+++ b/src/libexecline/deps-lib/execline
@@ -0,0 +1,21 @@
+el_execsequence.o
+el_getstrict.o
+el_obsolescent.o
+el_popenv.o
+el_pushenv.o
+el_semicolon.o
+el_spawn0.o
+el_spawn1.o
+el_substandrun.o
+el_substandrun_str.o
+el_substitute.o
+el_transform.o
+el_vardupl.o
+exlsn_define.o
+exlsn_elglob.o
+exlsn_import.o
+exlsn_multidefine.o
+exlsn_exlp.o
+exlsn_main.o
+exlsn_free.o
+exlp.o
diff --git a/src/libexecline/el_execsequence.c b/src/libexecline/el_execsequence.c
new file mode 100644
index 0000000..6b825af
--- /dev/null
+++ b/src/libexecline/el_execsequence.c
@@ -0,0 +1,41 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#ifdef EXECLINE_OLD_VARNAMES
+#include <skalibs/bytestr.h>
+#endif
+#include <skalibs/djbunix.h>
+#include <skalibs/env.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/uint.h>
+#include <execline/execline.h>
+
+void el_execsequence (char const *const *argv1, char const *const *argv2, char const *const *envp)
+{
+ if (!argv2[0])
+ {
+ pathexec0_run(argv1, envp) ;
+ strerr_dieexec(111, argv1[0]) ;
+ }
+ else
+ {
+ int wstat ;
+ unsigned int j = 2 ;
+#ifdef EXECLINE_OLD_VARNAMES
+ char fmt[UINT_FMT * 2 + 15] = "?=" ;
+#else
+ char fmt[UINT_FMT + 1] = "?=" ;
+#endif
+ pid_t pid = el_spawn0(argv1[0], argv1, envp) ;
+ if (!pid) strerr_warnwu2sys("spawn ", argv1[0]) ;
+ if (wait_pid(pid, &wstat) < 0)
+ strerr_diefu2sys(111, "wait for ", argv1[0]) ;
+ j += uint_fmt(fmt + j, wait_status(wstat)) ; fmt[j++] = 0 ;
+#ifdef EXECLINE_OLD_VARNAMES
+ byte_copy(fmt + j, 13, "LASTEXITCODE=") ; j += 13 ;
+ j += uint_fmt(fmt + j, wait_status(wstat)) ; fmt[j++] = 0 ;
+#endif
+ pathexec_r(argv2, envp, env_len(envp), fmt, j) ;
+ }
+ strerr_dieexec(111, argv2[0]) ;
+}
diff --git a/src/libexecline/el_getstrict.c b/src/libexecline/el_getstrict.c
new file mode 100644
index 0000000..091845c
--- /dev/null
+++ b/src/libexecline/el_getstrict.c
@@ -0,0 +1,18 @@
+/* ISC license. */
+
+#include <skalibs/env.h>
+#include <skalibs/uint.h>
+#include <execline/execline.h>
+
+unsigned int el_getstrict (void)
+{
+ static unsigned int strict = 0 ;
+ static int first = 1 ;
+ if (first)
+ {
+ char const *x = env_get("EXECLINE_STRICT") ;
+ first = 0 ;
+ if (x) uint0_scan(x, &strict) ;
+ }
+ return strict ;
+}
diff --git a/src/libexecline/el_obsolescent.c b/src/libexecline/el_obsolescent.c
new file mode 100644
index 0000000..42b8a11
--- /dev/null
+++ b/src/libexecline/el_obsolescent.c
@@ -0,0 +1,10 @@
+/* ISC license. */
+
+#include <skalibs/strerr2.h>
+#include <execline/execline.h>
+
+void el_obsolescent (void)
+{
+ if (el_getstrict())
+ strerr_warnw3x("this command is marked as obsolescent. Please update your script to use the ", PROG, "x command instead.") ;
+}
diff --git a/src/libexecline/el_popenv.c b/src/libexecline/el_popenv.c
new file mode 100644
index 0000000..3726506
--- /dev/null
+++ b/src/libexecline/el_popenv.c
@@ -0,0 +1,44 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/uint.h>
+#include <execline/execline.h>
+
+int el_popenv (stralloc *sa, char const *const *envp, unsigned int envlen, char const *const *list, unsigned int listlen)
+{
+ unsigned int i = 0, salen = sa->len, count = 0 ;
+ for (; i < envlen ; i++)
+ {
+ unsigned int equal, colon, n ;
+ unsigned int j = 0 ;
+ for (; j < listlen ; j++) if (str_start(envp[i], list[j])) break ;
+ if (j == listlen) goto copyit ;
+ j = str_len(list[j]) ;
+ colon = j + str_chr(envp[i] + j, ':') ;
+ equal = j + str_chr(envp[i] + j, '=') ;
+ if (!envp[i][equal]) goto badenv ;
+ if (colon >= equal) { count++ ; continue ; }
+ if (colon + 1 + uint_scan(envp[i] + colon + 1, &n) != equal) goto copyit ;
+ if (!n) goto copyit ;
+ if (!stralloc_catb(sa, envp[i], colon)) goto err ;
+ if (n > 1)
+ {
+ char fmt[UINT_FMT+1] = ":" ;
+ n = 1 + uint_fmt(fmt+1, n-1) ;
+ if (!stralloc_catb(sa, fmt, n)) goto err ;
+ }
+ if (!stralloc_catb(sa, envp[i] + equal, str_len(envp[i] + equal) + 1)) goto err ;
+ continue ;
+copyit:
+ if (!stralloc_catb(sa, envp[i], str_len(envp[i]) + 1)) goto err ;
+ }
+ return (int)count ;
+
+badenv :
+ errno = EINVAL ;
+err:
+ sa->len = salen ;
+ return -1 ;
+}
diff --git a/src/libexecline/el_pushenv.c b/src/libexecline/el_pushenv.c
new file mode 100644
index 0000000..9b9608d
--- /dev/null
+++ b/src/libexecline/el_pushenv.c
@@ -0,0 +1,49 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/uint.h>
+#include <execline/execline.h>
+
+int el_pushenv (stralloc *sa, char const *const *envp, unsigned int envlen, char const *const *list, unsigned int listlen)
+{
+ unsigned int i = 0, salen = sa->len, count = 0 ;
+ for (; i < envlen ; i++)
+ {
+ unsigned int equal, colon ;
+ unsigned int j = 0 ;
+ for (; j < listlen ; j++) if (str_start(envp[i], list[j])) break ;
+ if (j == listlen) goto copyit ;
+ count++ ;
+ j = str_len(list[j]) ;
+ colon = j + str_chr(envp[i] + j, ':') ;
+ equal = j + str_chr(envp[i] + j, '=') ;
+ if (!envp[i][equal]) goto badenv ;
+ if (colon >= equal)
+ {
+ if (!stralloc_catb(sa, envp[i], equal)
+ || !stralloc_catb(sa, ":1", 2)) goto err ;
+ }
+ else
+ {
+ char fmt[UINT_FMT+1] = ":" ;
+ unsigned int n ;
+ if (colon + 1 + uint_scan(envp[i] + colon + 1, &n) != equal) goto copyit ;
+ n = 1 + uint_fmt(fmt+1, n+1) ;
+ if (!stralloc_catb(sa, envp[i], colon)) goto err ;
+ if (!stralloc_catb(sa, fmt, n)) goto err ;
+ }
+ if (!stralloc_catb(sa, envp[i] + equal, str_len(envp[i] + equal) + 1)) goto err ;
+ continue ;
+copyit:
+ if (!stralloc_catb(sa, envp[i], str_len(envp[i]) + 1)) goto err ;
+ }
+ return (int)count ;
+
+badenv :
+ errno = EINVAL ;
+err:
+ sa->len = salen ;
+ return -1 ;
+}
diff --git a/src/libexecline/el_semicolon.c b/src/libexecline/el_semicolon.c
new file mode 100644
index 0000000..dc99daf
--- /dev/null
+++ b/src/libexecline/el_semicolon.c
@@ -0,0 +1,35 @@
+/* ISC license. */
+
+#include <skalibs/env.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/uint.h>
+#include <execline/execline.h>
+
+int el_semicolon (char const **argv)
+{
+ static unsigned int nblock = 0 ;
+ register int argc1 = 0 ;
+ nblock++ ;
+ for (;; argc1++, argv++)
+ {
+ register char const *arg = *argv ;
+ if (!arg) return argc1 + 1 ;
+ if ((arg[0] == EXECLINE_BLOCK_END_CHAR) && (!EXECLINE_BLOCK_END_CHAR || !arg[1])) return argc1 ;
+ else if (arg[0] == EXECLINE_BLOCK_QUOTE_CHAR) ++*argv ;
+ else
+ {
+ unsigned int strict = el_getstrict() ;
+ if (strict)
+ {
+ char fmt1[UINT_FMT] ;
+ char fmt2[UINT_FMT] ;
+ fmt1[uint_fmt(fmt1, nblock)] = 0 ;
+ fmt2[uint_fmt(fmt2, (unsigned int)argc1)] = 0 ;
+ if (strict >= 2)
+ strerr_dief6x(100, "unquoted argument ", arg, " at block ", fmt1, " position ", fmt2) ;
+ else
+ strerr_warnw6x("unquoted argument ", arg, " at block ", fmt1, " position ", fmt2) ;
+ }
+ }
+ }
+}
diff --git a/src/libexecline/el_spawn0.c b/src/libexecline/el_spawn0.c
new file mode 100644
index 0000000..4b7f50f
--- /dev/null
+++ b/src/libexecline/el_spawn0.c
@@ -0,0 +1,15 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <skalibs/djbunix.h>
+#include <execline/execline.h>
+
+pid_t el_spawn0 (char const *prog, char const *const *argv, char const *const *envp)
+{
+ if (!argv[0])
+ {
+ static char const *const newargv[2] = { "/bin/true", 0 } ;
+ return child_spawn0(newargv[0], newargv, 0) ;
+ }
+ else return child_spawn0(prog, argv, envp) ;
+}
diff --git a/src/libexecline/el_spawn1.c b/src/libexecline/el_spawn1.c
new file mode 100644
index 0000000..4785b92
--- /dev/null
+++ b/src/libexecline/el_spawn1.c
@@ -0,0 +1,15 @@
+/* ISC license. */
+
+#include <sys/types.h>
+#include <skalibs/djbunix.h>
+#include <execline/execline.h>
+
+pid_t el_spawn1 (char const *prog, char const *const *argv, char const *const *envp, int *fd, int w)
+{
+ if (!argv[0])
+ {
+ static char const *const newargv[2] = { "/bin/true", 0 } ;
+ return child_spawn1(newargv[0], newargv, 0, fd, w) ;
+ }
+ else return child_spawn1(prog, argv, envp, fd, w) ;
+}
diff --git a/src/libexecline/el_substandrun.c b/src/libexecline/el_substandrun.c
new file mode 100644
index 0000000..7dddbca
--- /dev/null
+++ b/src/libexecline/el_substandrun.c
@@ -0,0 +1,13 @@
+/* ISC license. */
+
+#include <skalibs/env.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/skamisc.h>
+#include "exlsn.h"
+
+void el_substandrun (int argc, char const *const *argv, char const *const *envp, exlsn_t *info)
+{
+ satmp.len = 0 ;
+ if (!env_string(&satmp, argv, (unsigned int)argc)) strerr_diefu1sys(111, "env_string") ;
+ el_substandrun_str(&satmp, 0, envp, info) ;
+}
diff --git a/src/libexecline/el_substandrun_str.c b/src/libexecline/el_substandrun_str.c
new file mode 100644
index 0000000..351ec9d
--- /dev/null
+++ b/src/libexecline/el_substandrun_str.c
@@ -0,0 +1,25 @@
+/* ISC license. */
+
+#include <skalibs/djbunix.h>
+#include <skalibs/env.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <execline/execline.h>
+#include "exlsn.h"
+
+void el_substandrun_str (stralloc *src, unsigned int srcbase, char const *const *envp, exlsn_t *info)
+{
+ stralloc dst = STRALLOC_ZERO ;
+ register int r = el_substitute(&dst, src->s + srcbase, src->len, info->vars.s, info->values.s, genalloc_s(elsubst_t, &info->data), genalloc_len(elsubst_t, &info->data)) ;
+ if (r < 0) strerr_diefu1sys(111, "el_substitute") ;
+ exlsn_free(info) ;
+ stralloc_free(src) ;
+ {
+ char const *v[r + 1] ;
+ if (!env_make(v, r, dst.s, dst.len)) strerr_diefu1sys(111, "env_make") ;
+ v[r] = 0 ;
+ pathexec0_run(v, envp) ;
+ }
+ strerr_dieexec(111, dst.s) ;
+}
diff --git a/src/libexecline/el_substitute.c b/src/libexecline/el_substitute.c
new file mode 100644
index 0000000..a94c16d
--- /dev/null
+++ b/src/libexecline/el_substitute.c
@@ -0,0 +1,179 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <execline/execline.h>
+
+typedef struct elsubsu_s elsubsu_t, *elsubsu_t_ref ;
+struct elsubsu_s
+{
+ elsubst_t const *subst ;
+ unsigned int pos ;
+} ;
+
+typedef struct subsuinfo_s subsuinfo_t, *subsuinfo_t_ref ;
+struct subsuinfo_s
+{
+ stralloc dst ;
+ stralloc sa ;
+ genalloc list ; /* array of elsubsu_t */
+ char const *values ;
+} ;
+
+#define SUBSUINFO_ZERO { .dst = STRALLOC_ZERO, .sa = STRALLOC_ZERO, .list = GENALLOC_ZERO, .values = 0 }
+
+#define TEST 0x80
+#define MARK 0x40
+#define KEEPESC 0x20
+#define INCRESC 0x10
+
+#define STATE 0x07
+#define INWORD 0x00
+#define INDOLL 0x01
+#define INDBR 0x02
+#define INVAR 0x03
+#define INVARBR 0x04
+#define ACCEPT 0x05
+
+static int parseword (stralloc *sa, genalloc *list, char const *s, char const *vars, elsubst_t const *substs, unsigned int nsubst)
+{
+ static char const class[5] = "\0\\${}" ;
+ static unsigned char const table[6][5] =
+ {
+ { ACCEPT, ACCEPT, ACCEPT, ACCEPT | TEST, ACCEPT },
+ { INWORD | KEEPESC | INCRESC, INWORD | INCRESC, INWORD | INCRESC, INWORD | TEST | INCRESC, INWORD | INCRESC },
+ { INDOLL | KEEPESC, INDOLL, INDOLL, INDOLL | TEST, INDOLL },
+ { INWORD, INDBR | KEEPESC, INWORD, INWORD | TEST, INWORD },
+ { INWORD, INWORD, INWORD, INWORD | TEST, INWORD | TEST },
+ { INWORD, INVAR | MARK | KEEPESC, INVARBR | MARK | KEEPESC, INVAR | KEEPESC, INVARBR | KEEPESC }
+ } ;
+
+ unsigned int mark = 0, pos = 0, offset = 0, esc = 0, salen = sa->len, listlen = genalloc_len(elsubsu_t, list) ;
+ unsigned char state = INWORD ;
+
+ while (state != ACCEPT)
+ {
+ int nopush = 0 ;
+ unsigned char c = table[byte_chr(class, 5, s[pos])][state] ;
+
+ if (c & TEST)
+ {
+ unsigned int supp = (state == INVARBR) ;
+ unsigned int i = 0 ;
+ for (; i < nsubst ; i++)
+ {
+ if (!str_diffn(vars + substs[i].var, s + mark, pos - mark) && !vars[substs[i].var + pos - mark])
+ {
+ sa->len -= esc >> 1 ; offset += esc >> 1 ;
+ if (esc & 1)
+ {
+ byte_copy(sa->s + mark - offset - 2 - supp, pos - mark + 1 + supp, sa->s + mark - offset + (esc>>1) - 1 - supp) ;
+ sa->len-- ; offset++ ;
+ }
+ else
+ {
+ elsubsu_t cur ;
+ cur.subst = substs + i ;
+ cur.pos = mark - offset - 1 - supp ;
+ if (!genalloc_append(elsubsu_t, list, &cur)) goto err ;
+ offset += sa->len - cur.pos ;
+ sa->len = cur.pos ;
+ if (supp) nopush = 1 ;
+ }
+ break ;
+ }
+ }
+ }
+ if (nopush) offset++ ;
+ else if (!stralloc_catb(sa, s+pos, 1)) goto err ;
+ if (c & MARK) mark = pos ;
+ if (!(c & KEEPESC)) esc = 0 ;
+ if (c & INCRESC) esc++ ;
+ state = c & STATE ; pos++ ;
+ }
+ sa->len-- ;
+ return (int)pos ;
+
+err:
+ sa->len = salen ;
+ list->len = listlen ;
+ return -1 ;
+}
+
+static int substword (subsuinfo_t *info, unsigned int wordstart, unsigned int wordlen, unsigned int n, unsigned int offset)
+{
+ if (n < genalloc_len(elsubsu_t, &info->list))
+ {
+ elsubsu_t *list = genalloc_s(elsubsu_t, &info->list) ;
+ char const *p = info->values + list[n].subst->value ;
+ unsigned int l = list[n].pos + offset ;
+ unsigned int dstbase = info->dst.len ;
+ unsigned int sabase = info->sa.len ;
+ unsigned int i = 0 ;
+ int nc = 0 ;
+ if (!stralloc_readyplus(&info->sa, l)) return -1 ;
+ stralloc_catb(&info->sa, info->sa.s + wordstart, l) ;
+ for ( ; i < list[n].subst->n ; i++)
+ {
+ int r ;
+ unsigned int plen = str_len(p) ;
+ info->sa.len = sabase + l ;
+ if (!stralloc_readyplus(&info->sa, plen + wordlen - l)) goto err ;
+ stralloc_catb(&info->sa, p, plen) ;
+ stralloc_catb(&info->sa, info->sa.s + wordstart + l, wordlen - l) ;
+ r = substword(info, sabase, info->sa.len - sabase, n+1, offset + plen) ;
+ if (r < 0) goto err ;
+ nc += r ;
+ p += plen+1 ;
+ }
+ return nc ;
+ err:
+ info->sa.len = sabase ;
+ info->dst.len = dstbase ;
+ return -1 ;
+ }
+ else
+ {
+ if (!stralloc_readyplus(&info->dst, wordlen+1)) return -1 ;
+ stralloc_catb(&info->dst, info->sa.s + wordstart, wordlen) ;
+ stralloc_0(&info->dst) ;
+ return 1 ;
+ }
+}
+
+int el_substitute (stralloc *dst, char const *src, unsigned int len, char const *vars, char const *values, elsubst_t const *substs, unsigned int nsubst)
+{
+ subsuinfo_t info = SUBSUINFO_ZERO ;
+ unsigned int nc = 0 ;
+ unsigned int i = 0 ;
+ unsigned int dstbase = dst->len ;
+ int wasnull = !dst->s ;
+ info.dst = *dst ;
+ info.values = values ;
+
+ while (i < len)
+ {
+ int r ;
+ genalloc_setlen(elsubsu_t, &info.list, 0) ;
+ info.sa.len = 0 ;
+ r = parseword(&info.sa, &info.list, src + i, vars, substs, nsubst) ;
+ if (r < 0) goto err ;
+ i += r ;
+ r = substword(&info, 0, info.sa.len, 0, 0) ;
+ if (r < 0) goto err ;
+ nc += r ;
+ }
+ genalloc_free(elsubsu_t, &info.list) ;
+ stralloc_free(&info.sa) ;
+ if (!wasnull) stralloc_free(dst) ;
+ *dst = info.dst ;
+ return (int)nc ;
+
+err :
+ genalloc_free(elsubsu_t, &info.list) ;
+ stralloc_free(&info.sa) ;
+ if (wasnull) stralloc_free(&info.dst) ; else info.dst.len = dstbase ;
+ *dst = info.dst ;
+ return -1 ;
+}
diff --git a/src/libexecline/el_transform.c b/src/libexecline/el_transform.c
new file mode 100644
index 0000000..ac84d1d
--- /dev/null
+++ b/src/libexecline/el_transform.c
@@ -0,0 +1,84 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/netstring.h>
+#include <skalibs/skamisc.h>
+#include <skalibs/stralloc.h>
+#include <execline/execline.h>
+
+static void el_crunch (stralloc *sa, unsigned int base, char const *delim)
+{
+ register unsigned int i = base, j = base ;
+ register int crunching = 0 ;
+ for (; i < sa->len ; i++)
+ {
+ if (!crunching) sa->s[j++] = sa->s[i] ;
+ if (delim[str_chr(delim, sa->s[i])]) crunching = 1 ;
+ else if (crunching)
+ {
+ i-- ;
+ crunching = 0 ;
+ }
+ }
+ sa->len = j ;
+}
+
+static int el_split (stralloc *sa, unsigned int base, eltransforminfo_t const *si, int chomped)
+{
+ unsigned int n = 0 ;
+ register unsigned int i = base ;
+ for (; i < sa->len ; i++)
+ if (si->delim[str_chr(si->delim, sa->s[i])])
+ {
+ sa->s[i] = 0 ;
+ n++ ;
+ base = i+1 ;
+ }
+
+ if (sa->len && sa->s[sa->len - 1])
+ {
+ if (si->chomp && !chomped) sa->len = base ;
+ else if (!stralloc_0(sa)) return -1 ;
+ else n++ ;
+ }
+ return n ;
+}
+
+static int el_splitnetstring (stralloc *sa, unsigned int base)
+{
+ unsigned int tmpbase = satmp.len ;
+ unsigned int n = 0, i = base ;
+ while (i < sa->len)
+ {
+ register int r = netstring_decode(&satmp, sa->s + i, sa->len - i) ;
+ if (r < 0) goto err ;
+ if (!stralloc_0(&satmp)) goto err ;
+ i += r ; n++ ;
+ }
+ sa->len = base ;
+ if (!stralloc_catb(sa, satmp.s + tmpbase, satmp.len - tmpbase))
+ {
+ sa->len = i ;
+ goto err ;
+ }
+ satmp.len = tmpbase ;
+ return n ;
+
+err:
+ satmp.len = tmpbase ;
+ return -1 ;
+}
+
+int el_transform (stralloc *sa, unsigned int i, eltransforminfo_t const *si)
+{
+ int chomped = 0 ;
+ if (si->crunch && *si->delim) el_crunch(sa, i, si->delim) ;
+ if (si->chomp && (sa->len > i)
+ && si->delim[str_chr(si->delim, sa->s[sa->len-1])])
+ {
+ sa->len-- ;
+ chomped = 1 ;
+ }
+ return si->split ? *si->delim ? el_split(sa, i, si, chomped) : el_splitnetstring(sa, i) :
+ stralloc_0(sa) ? 1 : -1 ;
+}
diff --git a/src/libexecline/el_vardupl.c b/src/libexecline/el_vardupl.c
new file mode 100644
index 0000000..7583669
--- /dev/null
+++ b/src/libexecline/el_vardupl.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <execline/execline.h>
+
+int el_vardupl (char const *key, char const *s, unsigned int len)
+{
+ register unsigned int i = 0 ;
+ for (; i < len ; i += str_len(s + i) + 1)
+ if (!str_diff(key, s + i)) return 1 ;
+ return 0 ;
+}
diff --git a/src/libexecline/exlp.c b/src/libexecline/exlp.c
new file mode 100644
index 0000000..060eb68
--- /dev/null
+++ b/src/libexecline/exlp.c
@@ -0,0 +1,78 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/env.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/uint.h>
+#include <execline/execline.h>
+#include "exlsn.h"
+
+int exlp (unsigned int nmin, char const *const *envp, exlsn_t *info)
+{
+ unsigned int varbase = info->vars.len ;
+ unsigned int valbase = info->values.len ;
+ unsigned int datbase = genalloc_len(elsubst_t, &info->data) ;
+ unsigned int i = 0 ;
+ char const *x = env_get2(envp, "#") ;
+ elsubst_t blah ;
+ unsigned int n, ntot, poszero ;
+ if (!x) return -2 ;
+ if (!uint0_scan(x, &n)) return -2 ;
+ if (el_vardupl("#", info->vars.s, info->vars.len)) return -2 ;
+ if (el_vardupl("@", info->vars.s, info->vars.len)) return -2 ;
+ {
+ register unsigned int strict = el_getstrict() ;
+ if (strict && (n < nmin))
+ {
+ char fmta[UINT_FMT] ;
+ char fmtn[UINT_FMT] ;
+ fmta[uint_fmt(fmta, n)] = 0 ;
+ fmtn[uint_fmt(fmtn, nmin)] = 0 ;
+ if (strict > 1)
+ strerr_dief4x(100, "too few arguments: expected at least ", fmtn, " but got ", fmta) ;
+ else
+ strerr_warnw4x("too few arguments: expected at least ", fmtn, " but got ", fmta) ;
+ }
+ }
+ blah.var = varbase ;
+ blah.value = info->values.len ;
+ blah.n = 1 ;
+ if (!stralloc_catb(&info->vars, "#\0@", 4)
+ || !stralloc_catb(&info->values, x, str_len(x) + 1)
+ || !genalloc_append(elsubst_t, &info->data, &blah)) goto err ;
+ ntot = n > nmin ? n : nmin ;
+ poszero = info->values.len ;
+ for (; i <= ntot ; i++)
+ {
+ char fmt[UINT_FMT] ;
+ unsigned int l = uint_fmt(fmt, i) ;
+ fmt[l] = 0 ;
+ if (el_vardupl(fmt, info->vars.s, info->vars.len)) goto err2 ;
+ x = (i <= n) ? env_get2(envp, fmt) : "" ;
+ if (!x) goto err2 ;
+ blah.var = info->vars.len ;
+ blah.value = info->values.len ;
+ blah.n = 1 ;
+ if (!stralloc_catb(&info->vars, fmt, l+1)
+ || !stralloc_catb(&info->values, x, str_len(x) + 1)
+ || !genalloc_append(elsubst_t, &info->data, &blah)) goto err ;
+ }
+ blah.var = varbase + 2 ;
+ blah.value = poszero + str_len(info->values.s + poszero) + 1 ;
+ blah.n = n ;
+ if (!genalloc_append(elsubst_t, &info->data, &blah)) goto err ;
+ return n ;
+
+ err:
+ info->vars.len = varbase ;
+ info->values.len = valbase ;
+ genalloc_setlen(elsubst_t, &info->data, datbase) ;
+ return -1 ;
+ err2:
+ info->vars.len = varbase ;
+ info->values.len = valbase ;
+ genalloc_setlen(elsubst_t, &info->data, datbase) ;
+ return -2 ;
+}
diff --git a/src/libexecline/exlsn_define.c b/src/libexecline/exlsn_define.c
new file mode 100644
index 0000000..3abe516
--- /dev/null
+++ b/src/libexecline/exlsn_define.c
@@ -0,0 +1,50 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <execline/execline.h>
+#include "exlsn.h"
+
+int exlsn_define (int argc, char const **argv, char const *const *envp, exlsn_t *info)
+{
+ eltransforminfo_t si = ELTRANSFORMINFO_ZERO ;
+ subgetopt_t localopt = SUBGETOPT_ZERO ;
+ elsubst_t blah ;
+ blah.var = info->vars.len ;
+ blah.value = info->values.len ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "nsCcd:", &localopt) ;
+ if (opt < 0) break ;
+ switch (opt)
+ {
+ case 'n' : si.chomp = 1 ; break ;
+ case 's' : si.split = 1 ; break ;
+ case 'C' : si.crunch = 1 ; break ;
+ case 'c' : si.crunch = 0 ; break ;
+ case 'd' : si.delim = localopt.arg ; break ;
+ default : return -3 ;
+ }
+ }
+ argc -= localopt.ind ; argv += localopt.ind ;
+
+ if (argc < 2) return -3 ;
+ if (!*argv[0] || el_vardupl(argv[0], info->vars.s, info->vars.len)) return -2 ;
+ if (!stralloc_catb(&info->vars, argv[0], str_len(argv[0]) + 1)) return -1 ;
+ if (!stralloc_cats(&info->values, argv[1])) goto err ;
+ {
+ register int r = el_transform(&info->values, blah.value, &si) ;
+ if (r < 0) goto err ;
+ blah.n = r ;
+ }
+ if (!genalloc_append(elsubst_t, &info->data, &blah)) goto err ;
+ (void)envp ;
+ return localopt.ind + 2 ;
+
+ err:
+ info->vars.len = blah.var ;
+ info->values.len = blah.value ;
+ return -1 ;
+}
diff --git a/src/libexecline/exlsn_elglob.c b/src/libexecline/exlsn_elglob.c
new file mode 100644
index 0000000..67e939b
--- /dev/null
+++ b/src/libexecline/exlsn_elglob.c
@@ -0,0 +1,78 @@
+/* ISC license. */
+
+#include <errno.h>
+#include <glob.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <execline/execline.h>
+#include "exlsn.h"
+
+static int elgloberrfunc (char const *s, int e)
+{
+ errno = e ;
+ strerr_warnw2sys("while globbing, error reading ", s) ;
+ return 0 ;
+}
+
+int exlsn_elglob (int argc, char const **argv, char const *const *envp, exlsn_t *info)
+{
+ glob_t pglob ;
+ subgetopt_t localopt = SUBGETOPT_ZERO ;
+ elsubst_t blah ;
+ int flags = GLOB_NOSORT | GLOB_NOCHECK ;
+ unsigned int i = 0 ;
+ int verbose = 0 ;
+ blah.var = info->vars.len ;
+ blah.value = info->values.len ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "vwsme0", &localopt) ;
+ if (opt < 0) break ;
+ switch (opt)
+ {
+ case 'v' : verbose = 1 ; break ;
+ case 'w' : flags |= GLOB_ERR ; break ;
+ case 's' : flags &= ~GLOB_NOSORT ; break ;
+ case 'm' : flags |= GLOB_MARK ; break ;
+ case 'e' : flags |= GLOB_NOESCAPE ; break ;
+ case '0' : flags &= ~GLOB_NOCHECK ; break ;
+ default : return -3 ;
+ }
+ }
+ argc -= localopt.ind ; argv += localopt.ind ;
+
+ if (argc < 2) return -3 ;
+ if (!*argv[0] || el_vardupl(argv[0], info->vars.s, info->vars.len)) return -2 ;
+ if (!stralloc_catb(&info->vars, argv[0], str_len(argv[0]) + 1)) return -1 ;
+
+ pglob.gl_offs = 0 ;
+ switch (glob(argv[1], flags, verbose ? &elgloberrfunc : 0, &pglob))
+ {
+ case 0 : break ;
+ case GLOB_NOMATCH:
+ {
+ pglob.gl_pathc = 0 ;
+ pglob.gl_pathv = 0 ;
+ break ;
+ }
+ default: goto err ;
+ }
+ for ( ; i < (unsigned int)pglob.gl_pathc ; i++)
+ if (!stralloc_catb(&info->values, pglob.gl_pathv[i], str_len(pglob.gl_pathv[i]) + 1))
+ goto globerr ;
+ blah.n = pglob.gl_pathc ;
+ globfree(&pglob) ;
+ if (!genalloc_append(elsubst_t, &info->data, &blah)) goto err ;
+ (void)envp ;
+ return localopt.ind + 2 ;
+
+ globerr:
+ globfree(&pglob) ;
+ err:
+ info->vars.len = blah.var ;
+ info->values.len = blah.value ;
+ return -1 ;
+}
diff --git a/src/libexecline/exlsn_exlp.c b/src/libexecline/exlsn_exlp.c
new file mode 100644
index 0000000..4ca513e
--- /dev/null
+++ b/src/libexecline/exlsn_exlp.c
@@ -0,0 +1,27 @@
+/* ISC license. */
+
+#include <skalibs/sgetopt.h>
+#include <skalibs/uint.h>
+#include "exlsn.h"
+
+int exlsn_exlp (int argc, char const **argv, char const *const *envp, exlsn_t *info)
+{
+ subgetopt_t localopt = SUBGETOPT_ZERO ;
+ unsigned int nmin = 0 ;
+ int n ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "P:", &localopt) ;
+ if (opt < 0) break ;
+ switch (opt)
+ {
+ case 'P' : if (uint0_scan(localopt.arg, &nmin)) break ;
+ default : return -3 ;
+ }
+ }
+ argc -= localopt.ind ; argv += localopt.ind ;
+ if (!argc) return -3 ;
+ n = exlp(nmin, envp, info) ;
+ if (n < 0) return n ;
+ return localopt.ind ;
+}
diff --git a/src/libexecline/exlsn_free.c b/src/libexecline/exlsn_free.c
new file mode 100644
index 0000000..4d9dde3
--- /dev/null
+++ b/src/libexecline/exlsn_free.c
@@ -0,0 +1,12 @@
+/* ISC license. */
+
+#include <skalibs/stralloc.h>
+#include "exlsn.h"
+
+void exlsn_free (exlsn_t *info)
+{
+ stralloc_free(&info->vars) ;
+ stralloc_free(&info->values) ;
+ stralloc_free(&info->data) ;
+}
+
diff --git a/src/libexecline/exlsn_import.c b/src/libexecline/exlsn_import.c
new file mode 100644
index 0000000..1574027
--- /dev/null
+++ b/src/libexecline/exlsn_import.c
@@ -0,0 +1,76 @@
+/* ISC license. */
+
+#include <skalibs/bytestr.h>
+#include <skalibs/sgetopt.h>
+#include <skalibs/strerr2.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <skalibs/env.h>
+#include <execline/execline.h>
+#include "exlsn.h"
+
+static int exlsn_import_as (int argc, char const **argv, char const *const *envp, exlsn_t *info, unsigned int as)
+{
+ eltransforminfo_t si = ELTRANSFORMINFO_ZERO ;
+ subgetopt_t localopt = SUBGETOPT_ZERO ;
+ elsubst_t blah ;
+ char const *defaultval = 0 ;
+ char const *x ;
+ int insist = 0 ;
+ blah.var = info->vars.len ;
+ blah.value = info->values.len ;
+
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "iD:nsCcd:", &localopt) ;
+ if (opt < 0) break ;
+ switch (opt)
+ {
+ case 'i' : insist = 1 ; break ;
+ case 'D' : defaultval = localopt.arg ; break ;
+ case 'n' : si.chomp = 1 ; break ;
+ case 's' : si.split = 1 ; break ;
+ case 'C' : si.crunch = 1 ; break ;
+ case 'c' : si.crunch = 0 ; break ;
+ case 'd' : si.delim = localopt.arg ; break ;
+ default : return -3 ;
+ }
+ }
+ argc -= localopt.ind ; argv += localopt.ind ;
+
+ if ((unsigned int)argc < 1+as) return -3 ;
+ if (!*argv[0] || el_vardupl(argv[0], info->vars.s, info->vars.len)) return -2 ;
+ if (!stralloc_catb(&info->vars, argv[0], str_len(argv[0]) + 1)) return -1 ;
+ x = env_get2(envp, argv[as]) ;
+ if (!x)
+ {
+ if (insist) strerr_dienotset(100, argv[as]) ;
+ x = defaultval ;
+ }
+ if (!x) blah.n = 0 ;
+ else
+ {
+ register int r ;
+ if (!stralloc_cats(&info->values, x)) goto err ;
+ r = el_transform(&info->values, blah.value, &si) ;
+ if (r < 0) goto err ;
+ blah.n = r ;
+ }
+ if (!genalloc_append(elsubst_t, &info->data, &blah)) goto err ;
+ return localopt.ind + 1 + as ;
+
+ err:
+ info->vars.len = blah.var ;
+ info->values.len = blah.value ;
+ return -1 ;
+}
+
+int exlsn_import (int argc, char const **argv, char const *const *envp, exlsn_t *info)
+{
+ return exlsn_import_as(argc, argv, envp, info, 0) ;
+}
+
+int exlsn_importas (int argc, char const **argv, char const *const *envp, exlsn_t *info)
+{
+ return exlsn_import_as(argc, argv, envp, info, 1) ;
+}
diff --git a/src/libexecline/exlsn_main.c b/src/libexecline/exlsn_main.c
new file mode 100644
index 0000000..e50fc2d
--- /dev/null
+++ b/src/libexecline/exlsn_main.c
@@ -0,0 +1,19 @@
+/* ISC license. */
+
+#include <skalibs/strerr2.h>
+#include <execline/execline.h>
+#include "exlsn.h"
+
+void exlsn_main (int argc, char const **argv, char const *const *envp, exlsnfunc_t *func, char const *usage)
+{
+ exlsn_t info = EXLSN_ZERO ;
+ int r = (*func)(argc, argv, envp, &info) ;
+ if (r < 0) switch (r)
+ {
+ case -3 : strerr_dieusage(100, usage) ;
+ case -2 : strerr_dief1x(111, "bad substitution key") ;
+ case -1 : strerr_diefu1sys(111, "complete exlsn function") ;
+ default : strerr_diefu2x(111, "complete exlsn function", ": unknown error") ;
+ }
+ el_substandrun(argc-r, argv+r, envp, &info) ;
+}
diff --git a/src/libexecline/exlsn_multidefine.c b/src/libexecline/exlsn_multidefine.c
new file mode 100644
index 0000000..e64cbfd
--- /dev/null
+++ b/src/libexecline/exlsn_multidefine.c
@@ -0,0 +1,79 @@
+/* ISC license. */
+
+#include <skalibs/sgetopt.h>
+#include <skalibs/bytestr.h>
+#include <skalibs/stralloc.h>
+#include <skalibs/genalloc.h>
+#include <execline/execline.h>
+#include "exlsn.h"
+
+int exlsn_multidefine (int argc, char const **argv, char const *const *envp, exlsn_t *info)
+{
+ eltransforminfo_t si = ELTRANSFORMINFO_ZERO ;
+ subgetopt_t localopt = SUBGETOPT_ZERO ;
+ unsigned int varbase = info->vars.len ;
+ unsigned int valbase = info->values.len ;
+ unsigned int pos = valbase ;
+ unsigned int i = 0 ;
+ unsigned int max ;
+ char const *x ;
+ int argc1 ;
+ int zeroword = 0, likeread = 0 ;
+ si.split = 1 ;
+ for (;;)
+ {
+ register int opt = subgetopt_r(argc, argv, "0rnCcd:", &localopt) ;
+ if (opt < 0) break ;
+ switch (opt)
+ {
+ case '0' : zeroword = 1 ; break ;
+ case 'r' : likeread = 1 ; break ;
+ case 'n' : si.chomp = 1 ; break ;
+ case 'C' : si.crunch = 1 ; break ;
+ case 'c' : si.crunch = 0 ; break ;
+ case 'd' : si.delim = localopt.arg ; break ;
+ default : return -3 ;
+ }
+ }
+ argc -= localopt.ind ; argv += localopt.ind ;
+
+ if (argc < 2) return -3 ;
+ x = argv[0] ;
+ argv++ ; argc-- ;
+ argc1 = el_semicolon(argv) ;
+ if (argc1 >= argc) return -3 ;
+ if (!stralloc_cats(&info->values, x)) return -1 ;
+ {
+ register int r = el_transform(&info->values, valbase, &si) ;
+ if (r < 0) goto err ;
+ max = r ;
+ }
+ if (!stralloc_0(&info->values)) goto err ;
+ for (; i < (unsigned int)argc1 ; i++)
+ {
+ if (*argv[i])
+ {
+ elsubst_t blah ;
+ blah.var = info->vars.len ;
+ if (el_vardupl(argv[i], info->vars.s, info->vars.len)) goto err2 ;
+ if (!stralloc_catb(&info->vars, argv[i], str_len(argv[i]) + 1)) goto err ;
+ blah.value = i < max ? pos : info->values.len - 1 ;
+ blah.n = (i < max) || !zeroword ;
+ if (!genalloc_append(elsubst_t, &info->data, &blah)) goto err ;
+ }
+ if (i < max) pos += str_len(info->values.s + pos) + 1 ;
+ }
+ if ((i < max) && likeread) genalloc_s(elsubst_t, &info->data)[i-1].n = max - i + 1 ;
+
+ (void)envp ;
+ return localopt.ind + argc1 + 2 ;
+
+ err:
+ info->vars.len = varbase ;
+ info->values.len = valbase ;
+ return -1 ;
+ err2:
+ info->vars.len = varbase ;
+ info->values.len = valbase ;
+ return -2 ;
+}