summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--doc/forbacktickx.html5
-rw-r--r--doc/forstdin.html78
-rw-r--r--doc/index.html3
-rw-r--r--doc/upgrade.html7
-rw-r--r--package/deps.mak10
-rw-r--r--package/info2
-rw-r--r--package/modes3
-rw-r--r--package/targets.mak1
-rw-r--r--src/execline/deps-exe/forbacktickx1
-rw-r--r--src/execline/deps-exe/forstdin2
-rw-r--r--src/execline/forbacktickx.c155
-rw-r--r--src/execline/forstdin.c157
-rw-r--r--src/include/execline/execline.h1
-rw-r--r--src/libexecline/deps-lib/execline1
-rw-r--r--src/libexecline/el_obsolescent.c10
15 files changed, 312 insertions, 124 deletions
diff --git a/doc/forbacktickx.html b/doc/forbacktickx.html
index 0780f3d..979a483 100644
--- a/doc/forbacktickx.html
+++ b/doc/forbacktickx.html
@@ -76,7 +76,10 @@ split <em>x</em>. </li>
 
 <ul>
  <li> You can start <em>loop...</em> with "import -u <em>variable</em>"
-to perform variable substitution.
+to perform variable substitution. </li>
+ <li> forbacktickx is now implemented as a trivial wrapper around the
+<a href="pipeline.html">pipeline</a> and
+<a href="forstdin.html">forstdin</a> commands. </li>
 </ul>
  
 </body>
diff --git a/doc/forstdin.html b/doc/forstdin.html
new file mode 100644
index 0000000..e4c5534
--- /dev/null
+++ b/doc/forstdin.html
@@ -0,0 +1,78 @@
+<html>
+ <head>
+  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+  <meta http-equiv="Content-Language" content="en" />
+  <title>execline: the forstdin command</title>
+  <meta name="Description" content="execline: the forstdin command" />
+  <meta name="Keywords" content="execline command forstdin" />
+  <!-- <link rel="stylesheet" type="text/css" href="http://skarnet.org/default.css" /> -->
+ </head>
+<body>
+
+<p>
+<a href="index.html">execline</a><br />
+<a href="http://skarnet.org/software/">Software</a><br />
+<a href="http://skarnet.org/">skarnet.org</a>
+</p>
+
+<h1> The <tt>forstdin</tt> program </h1>
+
+<p>
+<tt>forstdin</tt> uses its input as loop elements to
+run another program.
+</p>
+
+<h2> Interface </h2>
+
+<p>
+ In an <a href="execlineb.html">execlineb</a> script:
+</p>
+
+<pre>
+     forstdin [ -p | -o <em>okcodes</em> | -x <em>breakcodes</em> ] [ -n ] [ -C | -c ] [ -0 | -d <em>delim</em> ] <em>variable</em> <em>loop...</em>
+</pre>
+
+<ul>
+ <li> <tt>forstdin</tt> reads its standard input as it becomes available,
+<a href="el_transform.html#split">splitting</a> it automatically. </li>
+ <li> For every argument <em>x</em> in the split output,
+<tt>forstdin</tt> runs <em>loop...</em> as a child process, with
+<em>variable</em>=<em>x</em> added to its environment. </li>
+ <li><tt>forstdin</tt> then exits 0.
+</ul>
+
+<h2> Options </h2>
+
+<ul>
+ <li> <tt>-p</tt>&nbsp;: parallel mode. Do not wait for a <em>loop...</em>
+instance to finish before spawning the next one. <em>forbacktickx</em> will
+still wait for all instances of <em>loop</em> to terminate before
+exiting, though. </li>
+ <li> <tt>-0</tt>&nbsp;: accept null characters on its stdin,
+using them as delimiters. If this option and a <tt>-d</tt> option are
+used simultaneously, the rightmost one wins. </li>
+ <li> <tt>-o</tt>&nbsp;<em>okcodes</em>&nbsp;: <em>okcodes</em> must
+be a comma-separated list of exit codes. If the <tt>-p</tt> flag
+hasn't been given and <em>loop</em> exits with one of the codes in
+<em>okcodes</em>,
+forstdin will run the following instances of the loop, but if the exit code is
+not listed in <em>okcodes</em>, forstdin will exit immediately with an
+<a href="exitcodes.html">approximation</a> of the same exit code. </li>
+ <li> <tt>-x</tt>&nbsp;<em>breakcodes</em>&nbsp;: like the previous
+option, but with inverted meaning - the listed exit codes are codes
+that will make forstdin break the loop and exit, and the unlisted exit
+codes will make it keep looping. </li>
+ <li> Other options are used to <a href="el_transform.html">control
+the substitution mechanism</a> for every <em>x</em>. Of course, you can't
+split <em>x</em>. </li>
+</ul>
+
+<h2> Notes </h2>
+
+<ul>
+ <li> You can start <em>loop...</em> with "import -u <em>variable</em>"
+to perform variable substitution. </li>
+</ul>
+ 
+</body>
+</html>
diff --git a/doc/index.html b/doc/index.html
index 5b19477..bc2e020 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -66,7 +66,7 @@ library. </li>
 <h3> Download </h3>
 
 <ul>
- <li> The current released version of execline is <a href="execline-2.1.0.0.tar.gz">2.1.0.0</a>. </li>
+ <li> The current released version of execline is <a href="execline-2.1.1.0.tar.gz">2.1.1.0</a>. </li>
  <li> Alternatively, you can checkout a copy of the execline git repository:
 <pre> git clone git://git.skarnet.org/execline </pre> </li>
 </ul>
@@ -167,6 +167,7 @@ to your installation: the shebang lines for your system might be something like
 </p>
 <ul>
 <li><a href="forx.html">The <tt>forx</tt> program</a></li>
+<li><a href="forstdin.html">The <tt>forstdin</tt> program</a></li>
 <li><a href="forbacktickx.html">The <tt>forbacktickx</tt> program</a></li>
 <li><a href="loopwhilex.html">The <tt>loopwhilex</tt> program</a></li>
 </ul>
diff --git a/doc/upgrade.html b/doc/upgrade.html
index b66a215..ce0e74f 100644
--- a/doc/upgrade.html
+++ b/doc/upgrade.html
@@ -17,6 +17,13 @@
 
 <h1> What has changed in execline </h1>
 
+<h2> in 2.1.1.0 </h2>
+
+<ul>
+ <li> <a href="forstdin.html">forstdin</a> added </li>
+ <li> forbacktickx rewritten as a wrapper around pipeline and forstdin </li>
+</ul>
+
 <h2> in 2.1.0.0 </h2>
 
 <ul>
diff --git a/package/deps.mak b/package/deps.mak
index 4bf3d89..5c77ee5 100644
--- a/package/deps.mak
+++ b/package/deps.mak
@@ -22,6 +22,7 @@ src/execline/fdreserve.o src/execline/fdreserve.lo: src/execline/fdreserve.c
 src/execline/fdswap.o src/execline/fdswap.lo: src/execline/fdswap.c
 src/execline/forbacktickx.o src/execline/forbacktickx.lo: src/execline/forbacktickx.c src/include/execline/config.h src/include/execline/execline.h
 src/execline/foreground.o src/execline/foreground.lo: src/execline/foreground.c src/include/execline/execline.h
+src/execline/forstdin.o src/execline/forstdin.lo: src/execline/forstdin.c src/include/execline/config.h src/include/execline/execline.h
 src/execline/forx.o src/execline/forx.lo: src/execline/forx.c src/include/execline/config.h src/include/execline/execline.h
 src/execline/getpid.o src/execline/getpid.lo: src/execline/getpid.c
 src/execline/heredoc.o src/execline/heredoc.lo: src/execline/heredoc.c
@@ -46,7 +47,6 @@ src/execline/unexport.o src/execline/unexport.lo: src/execline/unexport.c
 src/execline/wait.o src/execline/wait.lo: src/execline/wait.c src/include/execline/execline.h
 src/libexecline/el_execsequence.o src/libexecline/el_execsequence.lo: src/libexecline/el_execsequence.c src/include/execline/execline.h
 src/libexecline/el_getstrict.o src/libexecline/el_getstrict.lo: src/libexecline/el_getstrict.c src/include/execline/execline.h
-src/libexecline/el_obsolescent.o src/libexecline/el_obsolescent.lo: src/libexecline/el_obsolescent.c src/include/execline/execline.h
 src/libexecline/el_popenv.o src/libexecline/el_popenv.lo: src/libexecline/el_popenv.c src/include/execline/execline.h
 src/libexecline/el_pushenv.o src/libexecline/el_pushenv.lo: src/libexecline/el_pushenv.c src/include/execline/execline.h
 src/libexecline/el_semicolon.o src/libexecline/el_semicolon.lo: src/libexecline/el_semicolon.c src/include/execline/execline.h
@@ -103,9 +103,11 @@ fdreserve: src/execline/fdreserve.o -lskarnet
 fdswap: private EXTRA_LIBS :=
 fdswap: src/execline/fdswap.o -lskarnet
 forbacktickx: private EXTRA_LIBS :=
-forbacktickx: src/execline/forbacktickx.o ${LIBEXECLINE} -lskarnet
+forbacktickx: src/execline/forbacktickx.o -lskarnet
 foreground: private EXTRA_LIBS :=
 foreground: src/execline/foreground.o ${LIBEXECLINE} -lskarnet
+forstdin: private EXTRA_LIBS :=
+forstdin: src/execline/forstdin.o ${LIBEXECLINE} -lskarnet
 forx: private EXTRA_LIBS :=
 forx: src/execline/forx.o ${LIBEXECLINE} -lskarnet
 getpid: private EXTRA_LIBS :=
@@ -150,5 +152,5 @@ unexport: private EXTRA_LIBS :=
 unexport: src/execline/unexport.o -lskarnet
 wait: private EXTRA_LIBS :=
 wait: src/execline/wait.o ${LIBEXECLINE} -lskarnet
-libexecline.a:  src/libexecline/el_execsequence.o src/libexecline/el_getstrict.o src/libexecline/el_obsolescent.o src/libexecline/el_popenv.o src/libexecline/el_pushenv.o src/libexecline/el_semicolon.o src/libexecline/el_spawn0.o src/libexecline/el_spawn1.o src/libexecline/el_substandrun.o src/libexecline/el_substandrun_str.o src/libexecline/el_substitute.o src/libexecline/el_transform.o src/libexecline/el_vardupl.o src/libexecline/exlsn_define.o src/libexecline/exlsn_elglob.o src/libexecline/exlsn_import.o src/libexecline/exlsn_multidefine.o src/libexecline/exlsn_exlp.o src/libexecline/exlsn_main.o src/libexecline/exlsn_free.o src/libexecline/exlp.o
-libexecline.so:  src/libexecline/el_execsequence.lo src/libexecline/el_getstrict.lo src/libexecline/el_obsolescent.lo src/libexecline/el_popenv.lo src/libexecline/el_pushenv.lo src/libexecline/el_semicolon.lo src/libexecline/el_spawn0.lo src/libexecline/el_spawn1.lo src/libexecline/el_substandrun.lo src/libexecline/el_substandrun_str.lo src/libexecline/el_substitute.lo src/libexecline/el_transform.lo src/libexecline/el_vardupl.lo src/libexecline/exlsn_define.lo src/libexecline/exlsn_elglob.lo src/libexecline/exlsn_import.lo src/libexecline/exlsn_multidefine.lo src/libexecline/exlsn_exlp.lo src/libexecline/exlsn_main.lo src/libexecline/exlsn_free.lo src/libexecline/exlp.lo
+libexecline.a:  src/libexecline/el_execsequence.o src/libexecline/el_getstrict.o src/libexecline/el_popenv.o src/libexecline/el_pushenv.o src/libexecline/el_semicolon.o src/libexecline/el_spawn0.o src/libexecline/el_spawn1.o src/libexecline/el_substandrun.o src/libexecline/el_substandrun_str.o src/libexecline/el_substitute.o src/libexecline/el_transform.o src/libexecline/el_vardupl.o src/libexecline/exlsn_define.o src/libexecline/exlsn_elglob.o src/libexecline/exlsn_import.o src/libexecline/exlsn_multidefine.o src/libexecline/exlsn_exlp.o src/libexecline/exlsn_main.o src/libexecline/exlsn_free.o src/libexecline/exlp.o
+libexecline.so:  src/libexecline/el_execsequence.lo src/libexecline/el_getstrict.lo src/libexecline/el_popenv.lo src/libexecline/el_pushenv.lo src/libexecline/el_semicolon.lo src/libexecline/el_spawn0.lo src/libexecline/el_spawn1.lo src/libexecline/el_substandrun.lo src/libexecline/el_substandrun_str.lo src/libexecline/el_substitute.lo src/libexecline/el_transform.lo src/libexecline/el_vardupl.lo src/libexecline/exlsn_define.lo src/libexecline/exlsn_elglob.lo src/libexecline/exlsn_import.lo src/libexecline/exlsn_multidefine.lo src/libexecline/exlsn_exlp.lo src/libexecline/exlsn_main.lo src/libexecline/exlsn_free.lo src/libexecline/exlp.lo
diff --git a/package/info b/package/info
index 54dedf4..f57cc18 100644
--- a/package/info
+++ b/package/info
@@ -1,4 +1,4 @@
 package=execline
-version=2.1.0.0
+version=2.1.1.0
 category=admin
 package_macro_name=EXECLINE
diff --git a/package/modes b/package/modes
index 332d2c0..8519ef5 100644
--- a/package/modes
+++ b/package/modes
@@ -18,9 +18,10 @@ fdclose			0755
 fdreserve		0755
 fdmove			0755
 fdswap			0755
-forx			0755
 forbacktickx		0755
 foreground		0755
+forstdin		0755
+forx			0755
 getpid			0755
 heredoc			0755
 homeof			0755
diff --git a/package/targets.mak b/package/targets.mak
index 2b1c0f4..9601f69 100644
--- a/package/targets.mak
+++ b/package/targets.mak
@@ -19,6 +19,7 @@ fdswap \
 fdreserve \
 forbacktickx \
 foreground \
+forstdin \
 forx \
 getpid \
 heredoc \
diff --git a/src/execline/deps-exe/forbacktickx b/src/execline/deps-exe/forbacktickx
index 97021b5..e7187fe 100644
--- a/src/execline/deps-exe/forbacktickx
+++ b/src/execline/deps-exe/forbacktickx
@@ -1,2 +1 @@
-${LIBEXECLINE}
 -lskarnet
diff --git a/src/execline/deps-exe/forstdin b/src/execline/deps-exe/forstdin
new file mode 100644
index 0000000..97021b5
--- /dev/null
+++ b/src/execline/deps-exe/forstdin
@@ -0,0 +1,2 @@
+${LIBEXECLINE}
+-lskarnet
diff --git a/src/execline/forbacktickx.c b/src/execline/forbacktickx.c
index 51f308e..0e58106 100644
--- a/src/execline/forbacktickx.c
+++ b/src/execline/forbacktickx.c
@@ -1,43 +1,23 @@
 /* ISC license. */
 
-#include <sys/types.h>
-#include <errno.h>
-#include <skalibs/sgetopt.h>
+#include <skalibs/ushort.h>
 #include <skalibs/bytestr.h>
-#include <skalibs/buffer.h>
-#include <skalibs/fmtscan.h>
+#include <skalibs/sgetopt.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 | -o okcode,okcode,... | -x breakcode,breakcode,... ] [ -n ] [ -C | -c ] [ -0 | -d delim ] var { backtickcmd... } command..."
 #define dieusage() strerr_dieusage(100, USAGE)
 
-static int isok (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 ;
-}
+#define DELIM_DEFAULT " \n\r\t"
 
 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 ;
-  pid_t pidw ;
-  int fd, argc1 ;
-  unsigned short okcodes[256] ;
-  unsigned int nbc = 0 ;
-  int crunch = 0, chomp = 0, not = 1 ;
+  char const *delim = DELIM_DEFAULT ;
+  char const *codes = 0 ;
+  int crunch = 0, chomp = 0, not = 1, par = 0 ;
   PROG = "forbacktickx" ;
   {
     subgetopt_t l = SUBGETOPT_ZERO ;
@@ -48,100 +28,69 @@ int main (int argc, char const **argv, char const *const *envp)
       switch (opt)
       {
 	case 'e' : break ; /* compat */
-        case 'p' :
-        {
-          if (!genalloc_ready(pid_t, &pids, 2))
-            strerr_diefu1sys(111, "genalloc_ready") ;
-          break ;
-        }
+        case 'p' : par = 1 ; 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 '0' : delim = 0 ; break ;
+        case 'd' : delim = l.arg ; break ;
         case 'o' :
-          not = 0 ;
+        {
+          unsigned short okcodes[256] ;
+          unsigned int nbc ;
           if (!ushort_scanlist(okcodes, 256, l.arg, &nbc)) dieusage() ;
+          codes = l.arg ;
+          not = 0 ;
           break ;
+        }
         case 'x' :
-          not = 1 ;
+        {
+          unsigned short okcodes[256] ;
+          unsigned int nbc ;
           if (!ushort_scanlist(okcodes, 256, l.arg, &nbc)) dieusage() ;
+          codes = l.arg ;
+          not = 1 ;
           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 ;
-  pidw = el_spawn1(argv[0], argv, envp, &fd, 1) ;
-  if (!pidw) strerr_diefu2sys(111, "spawn ", argv[0]) ;
+  if (!argv[0][0]) dieusage() ;
+  if (!argv[1][0]) strerr_dief1x(100, "empty block") ;
   {
-    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 (;;)
+    unsigned int m = 0, i = 1 ;
+    char const *newargv[argc + 15] ;
+    newargv[m++] = EXECLINE_BINPREFIX "pipeline" ;
+    newargv[m++] = "--" ;
+    while (argv[i] && argv[i][0] != EXECLINE_BLOCK_END_CHAR && (!EXECLINE_BLOCK_END_CHAR || (argv[i][0] && argv[i][1])))
+      newargv[m++] = argv[i++] ;
+    if (!argv[i]) strerr_dief1x(100, "unterminated block") ;
+    newargv[m++] = "" ; i++ ;
+    newargv[m++] = EXECLINE_BINPREFIX "unexport" ;
+    newargv[m++] = "!" ;
+    newargv[m++] = EXECLINE_BINPREFIX "forstdin" ;
+    if (par) newargv[m++] = "-p" ;
+    if (chomp) newargv[m++] = "-n" ;
+    if (crunch) newargv[m++] = "-C" ;
+    if (!delim) newargv[m++] = "-0" ;
+    else if (str_diff(delim, DELIM_DEFAULT))
     {
-      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 (not == isok(okcodes, nbc, wait_estatus(wstat)))
-          return wait_estatus(wstat) ;
-      }
+      newargv[m++] = "-d" ;
+      newargv[m++] = delim ;
+    }
+    if (codes)
+    {
+      newargv[m++] = not ? "-x" : "-o" ;
+      newargv[m++] = codes ;
     }
-    stralloc_free(&modif) ;
+    newargv[m++] = "--" ;
+    newargv[m++] = argv[0] ;
+    while (argv[i]) newargv[m++] = argv[i++] ;
+    newargv[m++] = 0 ;
+    pathexec_run(newargv[0], newargv, envp) ;
+    strerr_dieexec(111, newargv[0]) ;
   }
-  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") ;
-  return 0 ;
 }
diff --git a/src/execline/forstdin.c b/src/execline/forstdin.c
new file mode 100644
index 0000000..1e9d5e6
--- /dev/null
+++ b/src/execline/forstdin.c
@@ -0,0 +1,157 @@
+/* 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/sig.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 "forstdin [ -p | -o okcode,okcode,... | -x breakcode,breakcode,... ] [ -n ] [ -C | -c ] [ -0 | -d delim ] var command..."
+#define dieusage() strerr_dieusage(100, USAGE)
+
+static genalloc pids = GENALLOC_ZERO ; /* pid_t */
+
+static int isok (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 ;
+}
+
+static void parallel_sigchld_handler (int sig)
+{
+  pid_t *tab = genalloc_s(pid_t, &pids) ;
+  unsigned int len = genalloc_len(pid_t, &pids) ;
+  int wstat ;
+  for (;;)
+  {
+    register int r = wait_pids_nohang(tab, len, &wstat) ;
+    if (r <= 0) break ;
+    tab[r-1] = tab[--len] ;
+  }
+  genalloc_setlen(pid_t, &pids, len) ;
+  (void)sig ;
+}
+
+int main (int argc, char const **argv, char const *const *envp)
+{
+  char const *delim = " \n\r\t" ;
+  unsigned int delimlen = 4 ;
+  unsigned short okcodes[256] ;
+  unsigned int nbc = 0 ;
+  int crunch = 0, chomp = 0, not = 1 ;
+  PROG = "forstdin" ;
+  {
+    subgetopt_t l = SUBGETOPT_ZERO ;
+    for (;;)
+    {
+      register int opt = subgetopt_r(argc, argv, "pnCc0d:o:x:", &l) ;
+      if (opt == -1) break ;
+      switch (opt)
+      {
+        case 'p' :
+        {
+          if (!genalloc_ready(pid_t, &pids, 1))
+            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 'o' :
+          not = 0 ;
+          if (!ushort_scanlist(okcodes, 256, l.arg, &nbc)) dieusage() ;
+          break ;
+        case 'x' :
+          not = 1 ;
+          if (!ushort_scanlist(okcodes, 256, l.arg, &nbc)) dieusage() ;
+          break ;
+        default : dieusage() ;
+      }
+    }
+    argc -= l.ind ; argv += l.ind ;
+  }
+  if (argc < 2) dieusage() ;
+  {
+    stralloc modif = STRALLOC_ZERO ;
+    unsigned int envlen = env_len(envp) ;
+    unsigned int modifstart = str_len(argv[0])+1 ;
+    char const *newenv[envlen + 2] ;
+    if (!stralloc_ready(&modif, modifstart+1))
+      strerr_diefu1sys(111, "stralloc_ready") ;
+    byte_copy(modif.s, modifstart-1, argv[0]) ;
+    modif.s[modifstart-1] = '=' ;
+    if (pids.s)
+    {
+      if (sig_catch(SIGCHLD, &parallel_sigchld_handler) < 0)
+        strerr_diefu2sys(111, "install", " SIGCHLD handler") ;
+    }
+    for (;;)
+    {
+      pid_t pid ;
+      modif.len = modifstart ;
+      if (delimlen)
+      {
+        register int r = skagetlnsep(buffer_0, &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(buffer_0, &modif, &unread) <= 0)
+        {
+          if (netstring_okeof(buffer_0, 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[1], argv + 1, newenv) ;
+      if (!pid) strerr_diefu2sys(111, "spawn ", argv[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[1]) ;
+        if (not == isok(okcodes, nbc, wait_estatus(wstat)))
+          return wait_estatus(wstat) ;
+      }
+    }
+    stralloc_free(&modif) ;
+  }
+  if (pids.s)
+  {
+    if (sig_restore(SIGCHLD) < 0)
+      strerr_diefu2sys(111, "restore", " SIGCHLD handler") ;
+    if (!waitn(genalloc_s(pid_t, &pids), genalloc_len(pid_t, &pids)))
+      strerr_diefu1sys(111, "waitn") ;
+  }
+  return 0 ;
+}
diff --git a/src/include/execline/execline.h b/src/include/execline/execline.h
index a5c16ad..1f1ec68 100644
--- a/src/include/execline/execline.h
+++ b/src/include/execline/execline.h
@@ -15,7 +15,6 @@
 
 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 */
diff --git a/src/libexecline/deps-lib/execline b/src/libexecline/deps-lib/execline
index 69d483d..f3e4e49 100644
--- a/src/libexecline/deps-lib/execline
+++ b/src/libexecline/deps-lib/execline
@@ -1,6 +1,5 @@
 el_execsequence.o
 el_getstrict.o
-el_obsolescent.o
 el_popenv.o
 el_pushenv.o
 el_semicolon.o
diff --git a/src/libexecline/el_obsolescent.c b/src/libexecline/el_obsolescent.c
deleted file mode 100644
index 42b8a11..0000000
--- a/src/libexecline/el_obsolescent.c
+++ /dev/null
@@ -1,10 +0,0 @@
-/* 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.") ;
-}