about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLaurent Bercot <ska-skaware@skarnet.org>2020-11-24 17:56:57 +0000
committerLaurent Bercot <ska-skaware@skarnet.org>2020-11-24 17:56:57 +0000
commit49b387bb53e76eecd2b6cf4f89f3146fc2198bd3 (patch)
tree6dc1c9dc123a685f02ca29564fe6d58194cedfcb
parent6d217dbeac86c8e5a15fed4c7d3a58d81420d9b0 (diff)
downloadexecline-49b387bb53e76eecd2b6cf4f89f3146fc2198bd3.tar.gz
execline-49b387bb53e76eecd2b6cf4f89f3146fc2198bd3.tar.xz
execline-49b387bb53e76eecd2b6cf4f89f3146fc2198bd3.zip
Fix chomping: only make it default on line-processing binaries
-rw-r--r--NEWS4
-rw-r--r--doc/backtick.html6
-rw-r--r--doc/define.html2
-rw-r--r--doc/el_transform.html22
-rw-r--r--doc/forbacktickx.html34
-rw-r--r--doc/forstdin.html41
-rw-r--r--doc/importas.html2
-rw-r--r--doc/multidefine.html4
-rw-r--r--doc/multisubstitute.html4
-rw-r--r--doc/redirfd.html8
-rw-r--r--doc/upgrade.html7
-rw-r--r--doc/withstdinas.html5
-rw-r--r--src/execline/backtick.c7
-rw-r--r--src/execline/define.c2
-rw-r--r--src/execline/forbacktickx.c16
-rw-r--r--src/execline/forstdin.c12
-rw-r--r--src/execline/forx.c3
-rw-r--r--src/execline/ifthenelse.c2
-rw-r--r--src/execline/importas.c2
-rw-r--r--src/execline/multidefine.c2
-rw-r--r--src/execline/redirfd.c6
-rw-r--r--src/execline/tryexec.c2
-rw-r--r--src/execline/withstdinas.c7
-rw-r--r--src/libexecline/exlsn_define.c3
-rw-r--r--src/libexecline/exlsn_importas.c3
-rw-r--r--src/libexecline/exlsn_multidefine.c3
26 files changed, 132 insertions, 77 deletions
diff --git a/NEWS b/NEWS
index 7ef56a9..8058b77 100644
--- a/NEWS
+++ b/NEWS
@@ -5,6 +5,10 @@ In 2.7.0.0
 
  - forstdin QoL changes: now it exits 1 if it doesn't read anything,
 and it only splits on newlines by default.
+ - forbacktickx, which is a wrapper to forstdin, gets the same changes.
+ - Line-processing binaries now chomp by default. Substitution
+binaries do not.
+ - New -N option everywhere to disable chomping.
  - New "default" directive to trap, replacing the irrelevant "timeout".
 
 
diff --git a/doc/backtick.html b/doc/backtick.html
index b1e2870..1f77432 100644
--- a/doc/backtick.html
+++ b/doc/backtick.html
@@ -30,7 +30,7 @@ environment variable, then executes another program.
 </p>
 
 <pre>
-     backtick [ -i | -I | -D <em>default</em> ] [ -n ] <em>variable</em> { <em>prog1...</em> } <em>prog2...</em>
+     backtick [ -i | -I | -D <em>default</em> ] [ -N | -n ] <em>variable</em> { <em>prog1...</em> } <em>prog2...</em>
 </pre>
 
 <ul>
@@ -46,8 +46,8 @@ output as a value. </li>
 <h2> Options </h2>
 
 <ul>
- <li> <tt>-n</tt>&nbsp;: chomp an ending newline off <em>prog1...</em>'s
-output. </li>
+ <li> <tt>-N</tt>&nbsp;: store <em>prog1...</em>'s output as is, including the last newline, if any. </li>
+ <li> <tt>-n</tt>&nbsp;: chomp an ending newline off <em>prog1...</em>'s output. This is the default. </li>
 </ul>
 
 <p>
diff --git a/doc/define.html b/doc/define.html
index 4c09c4c..16bce61 100644
--- a/doc/define.html
+++ b/doc/define.html
@@ -26,7 +26,7 @@ another program.
 <h2> Interface </h2>
 
 <pre>
-     define [ -s ] [ -C | -c ] [ -n ] [ -d <em>delim</em> ] <em>variable</em> <em>value</em> <em>prog...</em>
+     define [ -s ] [ -C | -c ] [ -N | -n ] [ -d <em>delim</em> ] <em>variable</em> <em>value</em> <em>prog...</em>
 </pre>
 
 <ul>
diff --git a/doc/el_transform.html b/doc/el_transform.html
index cb163a8..e15ec69 100644
--- a/doc/el_transform.html
+++ b/doc/el_transform.html
@@ -41,6 +41,10 @@ is used, which means that the default delimiters are spaces, newlines,
 carriage returns and tabs.
 </p>
 
+<p>
+ (The <a href="forstdin.html">forstdin</a> command is a small exception:
+by default, it only recognizes newlines as delimiters.)
+</p>
 
 <a name="crunch">
 <h2> Crunching </h2>
@@ -53,6 +57,8 @@ three consecutive spaces, or a space and 4 tab characters, with a
 single space. This is called <em>crunching</em>, and it is done
 by giving the <tt>-C</tt> switch to the substitution command. The
 remaining delimiter will always be the first in the sequence.
+Chomping is <em>off</em> by default, or if you give the <tt>-c</tt>
+switch.
 </p>
 
 <p>
@@ -66,8 +72,10 @@ remaining delimiter will always be the first in the sequence.
 <p>
  Sometimes you don't want the last delimiter in a value.
  <em>Chomping</em> deletes the last character of a value if it is a
-delimiter. It can be requested by giving the <tt>-n</tt> switch to the
-substitution command. Note that chomping always happens <em>after</em>
+delimiter. It is requested by giving the <tt>-n</tt> switch to the
+substitution command. You can turn it off by giving the <tt>-N</tt>
+switch. It is off by default unless mentioned in the documentation
+page of specific binaries. Note that chomping always happens <em>after</em>
 crunching, which means you can use crunching+chomping to ignore, for
 instance, a set of trailing spaces.
 </p>
@@ -112,11 +120,11 @@ several advantages over the shell's:
 </p>
 
 <ul>
- <li> Splitting has to be explicitly requested, by specifying the
-<tt>-s</tt> option to commands that perform
-<a href="el_substitute.html">substitution</a>. By default,
-substitutions are performed as is, without interpreting the
-characters in the value. </li>
+ <li> Splitting is off by default, which means that substitutions
+are performed as is, without interpreting the characters in the
+value. In execline, splitting has to be explicitly requested
+by specifying the <tt>-s</tt> option to commands that perform
+<a href="el_substitute.html">substitution</a>. </li>
  <li> Positional parameters are never split, so that execline
 scripts can handle arguments the way the user intended to. To
 split <tt>$1</tt>, for instance, you have to ask for it
diff --git a/doc/forbacktickx.html b/doc/forbacktickx.html
index 9cbcd98..d28ad0b 100644
--- a/doc/forbacktickx.html
+++ b/doc/forbacktickx.html
@@ -30,7 +30,7 @@ run another program.
 </p>
 
 <pre>
-     forbacktickx [ -p | -o <em>okcodes</em> | -x <em>breakcodes</em> ] [ -n ] [ -C | -c ] [ -0 | -d <em>delim</em> ] <em>variable</em> { <em>gen...</em> } <em>loop...</em>
+     forbacktickx [ -p | -o <em>okcodes</em> | -x <em>breakcodes</em> ] [ -N | -n ] [ -C | -c ] [ -0 | -d <em>delim</em> ] <em>variable</em> { <em>gen...</em> } <em>loop...</em>
 </pre>
 
 <ul>
@@ -54,9 +54,6 @@ output must not contain a null character. </li>
 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 from <em>gen</em>'s output,
-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
@@ -68,9 +65,32 @@ not listed in <em>okcodes</em>, forbacktickx will exit immediately with an
 option, but with inverted meaning - the listed exit codes are codes
 that will make forbacktickx 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>
+
+<p>
+ Other options are similar (in name and functionality) to the switches
+passed to <a href="el_transform.html">control a substitution mechanism</a>,
+on purpose; however, <tt>forbacktickx</tt> does not call the substitution
+mechanism and has its own semantics for those options.
+</p>
+
+<ul>
+ <li> <tt>-N</tt>&nbsp;: store the whole line in <em>variable</em>,
+including the terminating newline (or other delimiter). </li>
+ <li> <tt>-n</tt>&nbsp;: chomp a terminating delimiter from the line from
+stdin before storing it into <em>variable</em>. This is the default. </li>
+ <li> <tt>-C</tt>&nbsp;: crunch. If there is an empty line (i.e. that
+only contains a delimiter), do not call <em>loop</em>. If this option is
+given, <em>and</em> chomping is active, <em>and</em> the last line of stdin
+is not terminated by a delimiter, then this last line will not be processed. </li>
+ <li> <tt>-c</tt>&nbsp;: do not crunch, call <em>loop</em> even if
+the line is empty. This is the default. </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>-d&nbsp;<em>delim</em></tt>&nbsp;: use the characters in string
+<em>delim</em> as delimiters for a line. Default is "<tt>\n</tt>", meaning
+the input is only split on newlines. </li>
 </ul>
 
 <h2> Notes </h2>
diff --git a/doc/forstdin.html b/doc/forstdin.html
index 44939fe..74644b4 100644
--- a/doc/forstdin.html
+++ b/doc/forstdin.html
@@ -30,7 +30,7 @@ run another program.
 </p>
 
 <pre>
-     forstdin [ -p | -o <em>okcodes</em> | -x <em>breakcodes</em> ] [ -E firsteofcode ] [ -e eofcode ] [ -n ] [ -C | -c ] [ -0 | -d <em>delim</em> ] <em>variable</em> <em>loop...</em>
+     forstdin [ -p | -o <em>okcodes</em> | -x <em>breakcodes</em> ] [ -N | -n ] [ -C | -c ] [ -0 | -d <em>delim</em> ] <em>variable</em> <em>loop...</em>
 </pre>
 
 <ul>
@@ -50,9 +50,6 @@ and 1 if it hasn't read anything. </li>
 instance to finish before spawning the next one. forstdin 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
@@ -64,15 +61,33 @@ not listed in <em>okcodes</em>, forstdin will exit immediately with an
 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> <tt>-E</tt>&nbsp;<em>firsteofcode</em>&nbsp;: if forstdin encounters
-EOF on its first attempt to read data, exit <em>firsteofcode</em>. Default is 1. </li>
- <li> <tt>-e</tt>&nbsp;<em>eofcode</em>&nbsp;: if forstdin has already
-read data, and encounters EOF, exit <em>eofcode</em>. Default is 0. </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>. The default delimiter for <tt>forstdin</tt> is a
-newline; you need a <tt>-d</tt> option to split <em>x</em> around
-other values. </li>
+</ul>
+
+<p>
+ Other options are similar (in name and functionality) to the switches
+passed to <a href="el_transform.html">control a substitution mechanism</a>,
+on purpose; however, <tt>forstdin</tt> does not call the substitution
+mechanism and has its own semantics for those options.
+</p>
+
+<ul>
+ <li> <tt>-N</tt>&nbsp;: store the whole line in <em>variable</em>,
+including the terminating newline (or other delimiter). </li>
+ <li> <tt>-n</tt>&nbsp;: chomp a terminating delimiter from the line from
+stdin before storing it into <em>variable</em>. This is the default. </li>
+ <li> <tt>-C</tt>&nbsp;: crunch. If there is an empty line (i.e. that
+only contains a delimiter), do not call <em>loop</em>. If this option is
+given, <em>and</em> chomping is active, <em>and</em> the last line of stdin
+is not terminated by a delimiter, then this last line will not be processed. </li>
+ <li> <tt>-c</tt>&nbsp;: do not crunch, call <em>loop</em> even if
+the line is empty. This is the default. </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>-d&nbsp;<em>delim</em></tt>&nbsp;: use the characters in string
+<em>delim</em> as delimiters for a line. Default is "<tt>\n</tt>", meaning
+the input is only split on newlines. </li>
+ <li>
 </ul>
 
 <h2> Notes </h2>
diff --git a/doc/importas.html b/doc/importas.html
index 4ac4a98..ac95e87 100644
--- a/doc/importas.html
+++ b/doc/importas.html
@@ -26,7 +26,7 @@ environment variable, then executes another program.
 <h2> Interface </h2>
 
 <pre>
-     importas [ -i | -D default ] [ -u ] [ -s ] [ -C | -c ] [ -n ] [ -d <em>delim</em> ] <em>variable</em> <em>envvar</em> <em>prog...</em>
+     importas [ -i | -D default ] [ -u ] [ -s ] [ -C | -c ] [ -N | -n ] [ -d <em>delim</em> ] <em>variable</em> <em>envvar</em> <em>prog...</em>
 </pre>
 
 <ul>
diff --git a/doc/multidefine.html b/doc/multidefine.html
index f3909a2..69ad388 100644
--- a/doc/multidefine.html
+++ b/doc/multidefine.html
@@ -30,7 +30,7 @@ then executes another program.
 </p>
 
 <pre>
-     multidefine [ -0 ] [ -r ] [ -C | -c ] [ -n ] [ -d <em>delim</em> ] <em>value</em> { <em>variables...</em> } <em>prog...</em>
+     multidefine [ -0 ] [ -r ] [ -C | -c ] [ -N | -n ] [ -d <em>delim</em> ] <em>value</em> { <em>variables...</em> } <em>prog...</em>
 </pre>
 
 <ul>
@@ -64,6 +64,8 @@ If there are more words in the split <em>value</em> than there are
 <em>variables</em> in the block, the last variable will be replaced with all
 the remaining words (and will be split). Without this option, the last variable
 is replaced with a single word, and the excess words are lost. </li>
+ <li> Other options are used to <a href="el_transform.html">control</a> the
+substitution mechanism. Note that the value is always split. </li>
 </ul>
 
 </body>
diff --git a/doc/multisubstitute.html b/doc/multisubstitute.html
index aebdde8..2cb38ee 100644
--- a/doc/multisubstitute.html
+++ b/doc/multisubstitute.html
@@ -32,8 +32,8 @@ its <em>argv</em>, then executes another program.
 <pre>
      multisubstitute
      {
-       [ <a href="define.html">define</a> [ -n ] [ -s ] [ -C | -c ] [ -d <em>delim</em> ] <em>variable</em> <em>value</em> ]
-       [ <a href="importas.html">importas</a> [ -i | -D <em>default</em> ] [ -n ] [ -s ] [ -C | -c ] [ -d <em>delim</em> ] <em>variable</em> <em>envvar</em> ]
+       [ <a href="define.html">define</a> [ -N | -n ] [ -s ] [ -C | -c ] [ -d <em>delim</em> ] <em>variable</em> <em>value</em> ]
+       [ <a href="importas.html">importas</a> [ -i | -D <em>default</em> ] [ -N | -n ] [ -s ] [ -C | -c ] [ -d <em>delim</em> ] <em>variable</em> <em>envvar</em> ]
        [ <a href="elglob.html">elglob</a> [ -v ] [ -w ] [ -s ] [ -m ] [ -e ] [ -0 ] <em>variable</em> <em>pattern</em> ]
        [ <a href="elgetpositionals.html">elgetpositionals</a> [ -P <em>sharp</em> ] ]
        [ <a href="multidefine.html">multidefine</a> <em>value</em> { <em>variable...</em> } ]
diff --git a/doc/redirfd.html b/doc/redirfd.html
index bb5a5fb..9dbe409 100644
--- a/doc/redirfd.html
+++ b/doc/redirfd.html
@@ -26,7 +26,7 @@ executes a program.
 <h2> Interface </h2>
 
 <pre>
-     redirfd [ -r | -w | -u | -a | -c | -x ] [ -n ] [ -b ] <em>fd</em> <em>file</em> <em>prog...</em>
+     redirfd [ -r | -w | -u | -a | -x ] [ -n ] [ -b ] <em>fd</em> <em>file</em> <em>prog...</em>
 </pre>
 
 <p>
@@ -37,7 +37,7 @@ to <em>file</em>, then execs into <em>prog...</em>.
 <h2> Options </h2>
 
 <p>
- One and only one of the -r, -w, -u, -a, -c, or -x options must be given;
+ One and only one of the -r, -w, -u, -a, or -x options must be given;
 the -n and -b options may be added in any case.
 </p>
 
@@ -46,7 +46,6 @@ the -n and -b options may be added in any case.
  <li> <tt>-w</tt>&nbsp;: open <em>file</em> for writing, truncating it if it already exists. </li>
  <li> <tt>-u</tt>&nbsp;: open <em>file</em> for reading and writing. </li>
  <li> <tt>-a</tt>&nbsp;: open <em>file</em> for appending, creating it if it doesn't exist. </li>
- <li> <tt>-c</tt>&nbsp;: open <em>file</em> for appending. Do not create it if it doesn't exist. </li>
  <li> <tt>-x</tt>&nbsp;: open <em>file</em> for writing, creating it, failing if it already exists. </li>
  <li> <tt>-n</tt>&nbsp;: open <em>file</em> in non-blocking mode. </li>
  <li> <tt>-b</tt>&nbsp;: change mode of <em>file</em> after opening it:
@@ -65,9 +64,6 @@ to blocking mode if it was. </li>
 <tt>sh -c 'exec prog... <em>n</em>&lt;&gt;<em>file</em>'</tt></li>
  <li> <tt>redirfd -a <em>n</em> <em>file</em> prog...</tt> is roughly equivalent to
 <tt>sh -c 'exec prog... <em>n</em>&gt;&gt;<em>file</em>'</tt></li>
- <li> <tt>redirfd -c <em>n</em> <em>file</em> prog...</tt> has no portable
-shell equivalent. Some shells provide the <em>noclobber</em> option for
-a similar feature. </li>
  <li> <tt>redirfd -x <em>n</em> <em>file</em> prog...</tt> has no portable
 shell equivalent. </li>
 </ul>
diff --git a/doc/upgrade.html b/doc/upgrade.html
index d12b051..2d6172c 100644
--- a/doc/upgrade.html
+++ b/doc/upgrade.html
@@ -24,7 +24,12 @@
  <li> <a href="//skarnet.org/software/skalibs/">skalibs</a>
 dependency bumped to 2.9.4.0. </li>
  <li> <a href="forstdin.html">forstdin</a> now exits 1 on immediate EOF,
-and only splits on newlines by default.  </li>
+and only splits on newlines by default. </li>
+ <li> <a href="forbacktickx.html">forbacktickx</a>, which is a wrapper
+around <a href="forstdin.html">forstdin</a>, gets the same changes. </li>
+ <li> Input-processing binaries (<em>not</em> substitution binaries!)
+now chomp by default. </li>
+ <li> New <tt>-N</tt> option to deactivate chomping. </li>
  <li> New <tt>default</tt> directive to <a href="trap.html">trap</a>,
 replacing the <tt>timeout</tt> one, which was ill-suited to that program. </li>
 </ul>
diff --git a/doc/withstdinas.html b/doc/withstdinas.html
index f0db24a..ccd69d7 100644
--- a/doc/withstdinas.html
+++ b/doc/withstdinas.html
@@ -31,7 +31,7 @@ environment variable.
 </p>
 
 <pre>
-     withstdinas [ -i | -I | -D <em>default</em> ] [ -n ] <em>variable</em> <em>prog...</em>
+     withstdinas [ -i | -I | -D <em>default</em> ] [ -N | -n ] <em>variable</em> <em>prog...</em>
 </pre>
 
 <ul>
@@ -44,7 +44,8 @@ environment variable.
 <h2> Options </h2>
 
 <ul>
- <li> <tt>-n</tt>&nbsp;: chomp an ending newline off stdin. </li>
+ <li> <tt>-N</tt>&nbsp;: do not chomp an ending newline off stdin. </li>
+ <li> <tt>-n</tt>&nbsp;: chomp an ending newline off stdin. This is the default. </li>
 </ul>
 
 <p>
diff --git a/src/execline/backtick.c b/src/execline/backtick.c
index 94421b1..ea2399f 100644
--- a/src/execline/backtick.c
+++ b/src/execline/backtick.c
@@ -10,7 +10,7 @@
 #include <skalibs/djbunix.h>
 #include <execline/execline.h>
 
-#define USAGE "backtick [ -i | -I | -D default ] [ -n ] var { prog... } remainder..."
+#define USAGE "backtick [ -i | -I | -D default ] [ -N | -n ] var { prog... } remainder..."
 #define dieusage() strerr_dieusage(100, USAGE)
 
 int main (int argc, char const **argv, char const *const *envp)
@@ -20,17 +20,18 @@ int main (int argc, char const **argv, char const *const *envp)
   int argc1, fdwstat ;
   stralloc modif = STRALLOC_ZERO ;
   size_t modifstart ;
-  int insist = 0, chomp = 0 ;
+  int insist = 0, chomp = 1 ;
   char const *def = 0 ;
   PROG = "backtick" ;
   for (;;)
   {
-    int opt = subgetopt_r(argc, argv, "iInD:", &localopt) ;
+    int opt = subgetopt_r(argc, argv, "iINnD:", &localopt) ;
     if (opt < 0) break ;
     switch (opt)
     {
       case 'i' : insist = 2 ; break ;
       case 'I' : insist = 1 ; break ;
+      case 'N' : chomp = 0 ; break ;
       case 'n' : chomp = 1 ; break ;
       case 'D' : def = localopt.arg ; break ;
       default : dieusage() ;
diff --git a/src/execline/define.c b/src/execline/define.c
index f87c531..52ee67d 100644
--- a/src/execline/define.c
+++ b/src/execline/define.c
@@ -3,7 +3,7 @@
 #include <skalibs/strerr2.h>
 #include "exlsn.h"
 
-#define USAGE "define [ -n ] [ -s ] [ -C | -c ] [ -d delim ] key value prog..."
+#define USAGE "define [ -N | -n ] [ -s ] [ -C | -c ] [ -d delim ] key value prog..."
 
 int main (int argc, char const **argv, char const *const *envp)
 {
diff --git a/src/execline/forbacktickx.c b/src/execline/forbacktickx.c
index 8b31d92..3e07442 100644
--- a/src/execline/forbacktickx.c
+++ b/src/execline/forbacktickx.c
@@ -10,27 +10,25 @@
 #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 USAGE "forbacktickx [ -p | -o okcode,okcode,... | -x breakcode,breakcode,... ] [ -N | -n ] [ -C | -c ] [ -0 | -d delim ] var { backtickcmd... } command..."
 #define dieusage() strerr_dieusage(100, USAGE)
 
-#define DELIM_DEFAULT " \n\r\t"
-
 int main (int argc, char const *const *argv, char const *const *envp)
 {
-  char const *delim = DELIM_DEFAULT ;
+  char const *delim = "\n" ;
   char const *codes = 0 ;
-  int crunch = 0, chomp = 0, not = 1, par = 0 ;
+  int crunch = 0, chomp = 1, not = 1, par = 0 ;
   PROG = "forbacktickx" ;
   {
     subgetopt_t l = SUBGETOPT_ZERO ;
     for (;;)
     {
-      int opt = subgetopt_r(argc, argv, "epnCc0d:o:x:", &l) ;
+      int opt = subgetopt_r(argc, argv, "pNnCc0d:o:x:", &l) ;
       if (opt == -1) break ;
       switch (opt)
       {
-	case 'e' : break ; /* compat */
         case 'p' : par = 1 ; break ;
+        case 'N' : chomp = 0 ; break ;
         case 'n' : chomp = 1 ; break ;
         case 'C' : crunch = 1 ; break ;
         case 'c' : crunch = 0 ; break ;
@@ -82,10 +80,10 @@ int main (int argc, char const *const *argv, char const *const *envp)
     newargv[m++] = "!" ;
     newargv[m++] = EXECLINE_BINPREFIX "forstdin" ;
     if (par) newargv[m++] = "-p" ;
-    if (chomp) newargv[m++] = "-n" ;
+    newargv[m++] = chomp ? "-n" : "-N" ;
     if (crunch) newargv[m++] = "-C" ;
     if (!delim) newargv[m++] = "-0" ;
-    else if (strcmp(delim, DELIM_DEFAULT))
+    else if (strcmp(delim, "\n"))
     {
       newargv[m++] = "-d" ;
       newargv[m++] = delim ;
diff --git a/src/execline/forstdin.c b/src/execline/forstdin.c
index b907ef2..d57406a 100644
--- a/src/execline/forstdin.c
+++ b/src/execline/forstdin.c
@@ -15,10 +15,11 @@
 #include <skalibs/djbunix.h>
 #include <skalibs/skamisc.h>
 #include <skalibs/netstring.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 USAGE "forstdin [ -p | -o okcode,okcode,... | -x breakcode,breakcode,... ] [ -N | -n ] [ -C | -c ] [ -0 | -d delim ] var command..."
 #define dieusage() strerr_dieusage(100, USAGE)
 
 static genalloc pids = GENALLOC_ZERO ; /* pid_t */
@@ -51,13 +52,13 @@ int main (int argc, char const **argv, char const *const *envp)
   size_t delimlen = 1 ;
   size_t nbc = 0 ;
   unsigned short okcodes[256] ;
-  int crunch = 0, chomp = 0, not = 1, eofcode = 1 ;
+  int crunch = 0, chomp = 1, not = 1, eofcode = 1 ;
   PROG = "forstdin" ;
   {
     subgetopt_t l = SUBGETOPT_ZERO ;
     for (;;)
     {
-      int opt = subgetopt_r(argc, argv, "pnCc0d:o:x:", &l) ;
+      int opt = subgetopt_r(argc, argv, "pNnCc0d:o:x:", &l) ;
       if (opt == -1) break ;
       switch (opt)
       {
@@ -67,6 +68,7 @@ int main (int argc, char const **argv, char const *const *envp)
             strerr_diefu1sys(111, "genalloc_ready") ;
           break ;
         }
+        case 'N' : chomp = 0 ; break ;
         case 'n' : chomp = 1 ; break ;
         case 'C' : crunch = 1 ; break ;
         case 'c' : crunch = 0 ; break ;
@@ -113,7 +115,8 @@ int main (int argc, char const **argv, char const *const *envp)
           if (errno != EPIPE) strerr_diefu1sys(111, "skagetlnsep") ;
           if (chomp) break ;
         }
-        else modif.len-- ;
+        if (crunch && modif.len == modifstart + 1) continue ;
+        if (chomp) modif.len-- ;
       }
       else
       {
@@ -125,7 +128,6 @@ int main (int argc, char const **argv, char const *const *envp)
         }
       }
       eofcode = 0 ;
-      if (crunch && modif.len == modifstart) continue ;
       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") ;
diff --git a/src/execline/forx.c b/src/execline/forx.c
index 06e6b39..8312704 100644
--- a/src/execline/forx.c
+++ b/src/execline/forx.c
@@ -53,11 +53,10 @@ int main (int argc, char const **argv, char const *const *envp)
     subgetopt_t l = SUBGETOPT_ZERO ;
     for (;;)
     {
-      int opt = subgetopt_r(argc, argv, "epo:x:", &l) ;
+      int opt = subgetopt_r(argc, argv, "po:x:", &l) ;
       if (opt == -1) break ;
       switch (opt)
       {
-        case 'e' : break ; /* compat */
         case 'p' : flagpar = 1 ; break ;
         case 'o' :
           not = 0 ;
diff --git a/src/execline/ifthenelse.c b/src/execline/ifthenelse.c
index 86dbfe7..f0d75f9 100644
--- a/src/execline/ifthenelse.c
+++ b/src/execline/ifthenelse.c
@@ -59,7 +59,7 @@ int main (int argc, char const **argv, char const *const *envp)
       argv += argc2 + 1 ;
       argc2 = argc3 ;
     }
-    if (magicscope)  /* undocumented voodoo - dangerous and powerful */
+    if (magicscope)  /* undocumented on purpose: powerful but dangerous */
     {
       unsigned int i = 0 ;
       for (; remainder[i] ; i++) argv[argc2+i] = remainder[i] ;
diff --git a/src/execline/importas.c b/src/execline/importas.c
index 4011c83..bf20c64 100644
--- a/src/execline/importas.c
+++ b/src/execline/importas.c
@@ -3,7 +3,7 @@
 #include <skalibs/strerr2.h>
 #include "exlsn.h"
 
-#define USAGE "importas [ -i | -D default ] [ -u ] [ -n ] [ -s ] [ -C | -c ] [ -d delim ] key var prog..."
+#define USAGE "importas [ -i | -D default ] [ -u ] [ -N | -n ] [ -s ] [ -C | -c ] [ -d delim ] key var prog..."
 
 int main (int argc, char const **argv, char const *const *envp)
 {
diff --git a/src/execline/multidefine.c b/src/execline/multidefine.c
index 9665418..b5685f5 100644
--- a/src/execline/multidefine.c
+++ b/src/execline/multidefine.c
@@ -3,7 +3,7 @@
 #include <skalibs/strerr2.h>
 #include "exlsn.h"
 
-#define USAGE "multidefine [ -0 ] [ -r ] [ -n ] [ -C | -c ] [ -d delim ] value { vars... } prog..."
+#define USAGE "multidefine [ -0 ] [ -r ] [ -N | -n ] [ -C | -c ] [ -d delim ] value { vars... } prog..."
 
 int main (int argc, char const **argv, char const *const *envp)
 {
diff --git a/src/execline/redirfd.c b/src/execline/redirfd.c
index 93e1582..a34eaf7 100644
--- a/src/execline/redirfd.c
+++ b/src/execline/redirfd.c
@@ -2,12 +2,13 @@
 
 #include <fcntl.h>
 #include <errno.h>
+
 #include <skalibs/sgetopt.h>
 #include <skalibs/types.h>
 #include <skalibs/strerr2.h>
 #include <skalibs/djbunix.h>
 
-#define USAGE "redirfd -[ r | w | u | a | c | x ] [ -n ] [ -b ] fd file prog..."
+#define USAGE "redirfd -[ r | w | u | a | x ] [ -n ] [ -b ] fd file prog..."
 #define dieusage() strerr_dieusage(100, USAGE)
 
 int main (int argc, char const *const *argv, char const *const *envp)
@@ -21,7 +22,7 @@ int main (int argc, char const *const *argv, char const *const *envp)
     subgetopt_t l = SUBGETOPT_ZERO ;
     for (;;)
     {
-      int opt = subgetopt_r(argc, argv, "rwuacxnb", &l) ;
+      int opt = subgetopt_r(argc, argv, "rwuaxnb", &l) ;
       if (opt == -1) break ;
       switch (opt)
       {
@@ -29,7 +30,6 @@ int main (int argc, char const *const *argv, char const *const *envp)
         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 ;
diff --git a/src/execline/tryexec.c b/src/execline/tryexec.c
index 0b1ca87..f35312f 100644
--- a/src/execline/tryexec.c
+++ b/src/execline/tryexec.c
@@ -6,7 +6,7 @@
 #include <skalibs/djbunix.h>
 #include <execline/execline.h>
 
-#define USAGE "tryexec [ -n ] [ -c ] [ -l ] [ -a argv0 ] { command... }"
+#define USAGE "tryexec [ -n ] [ -c ] [ -l ] [ -a argv0 ] { command... } remainder..."
 
 int main (int argc, char const **argv, char const *const *envp)
 {
diff --git a/src/execline/withstdinas.c b/src/execline/withstdinas.c
index 1118fa3..2dfcdf4 100644
--- a/src/execline/withstdinas.c
+++ b/src/execline/withstdinas.c
@@ -9,7 +9,7 @@
 #include <skalibs/env.h>
 #include <skalibs/djbunix.h>
 
-#define USAGE "withstdinas [ -i | -I | -D default ] [ -n ] var remainder..."
+#define USAGE "withstdinas [ -i | -I | -D default ] [ -N | -n ] var remainder..."
 #define dieusage() strerr_dieusage(100, USAGE)
 
 int main (int argc, char const **argv, char const *const *envp)
@@ -17,17 +17,18 @@ int main (int argc, char const **argv, char const *const *envp)
   subgetopt_t localopt = SUBGETOPT_ZERO ;
   stralloc modif = STRALLOC_ZERO ;
   size_t modifstart ;
-  int insist = 0, chomp = 0 ;
+  int insist = 0, chomp = 1 ;
   char const *def = 0 ;
   PROG = "withstdinas" ;
   for (;;)
   {
-    int opt = subgetopt_r(argc, argv, "iInD:", &localopt) ;
+    int opt = subgetopt_r(argc, argv, "iINnD:", &localopt) ;
     if (opt < 0) break ;
     switch (opt)
     {
       case 'i' : insist = 2 ; break ;
       case 'I' : insist = 1 ; break ;
+      case 'N' : chomp = 0 ; break ;
       case 'n' : chomp = 1 ; break ;
       case 'D' : def = localopt.arg ; break ;
       default : dieusage() ;
diff --git a/src/libexecline/exlsn_define.c b/src/libexecline/exlsn_define.c
index 3e7429c..f2f5c4a 100644
--- a/src/libexecline/exlsn_define.c
+++ b/src/libexecline/exlsn_define.c
@@ -16,10 +16,11 @@ int exlsn_define (int argc, char const **argv, char const *const *envp, exlsn_t
   blah.value = info->values.len ;
   for (;;)
   {
-    int opt = subgetopt_r(argc, argv, "nsCcd:", &localopt) ;
+    int opt = subgetopt_r(argc, argv, "NnsCcd:", &localopt) ;
     if (opt < 0) break ;
     switch (opt)
     {
+      case 'N' : si.chomp = 0 ; break ;
       case 'n' : si.chomp = 1 ; break ;
       case 's' : si.split = 1 ; break ;
       case 'C' : si.crunch = 1 ; break ;
diff --git a/src/libexecline/exlsn_importas.c b/src/libexecline/exlsn_importas.c
index d8d9ff6..f23c055 100644
--- a/src/libexecline/exlsn_importas.c
+++ b/src/libexecline/exlsn_importas.c
@@ -23,13 +23,14 @@ int exlsn_importas (int argc, char const **argv, char const *const *envp, exlsn_
 
   for (;;)
   {
-    int opt = subgetopt_r(argc, argv, "iuD:nsCcd:", &localopt) ;
+    int opt = subgetopt_r(argc, argv, "iuD:NnsCcd:", &localopt) ;
     if (opt < 0) break ;
     switch (opt)
     {
       case 'i' : insist = 1 ; break ;
       case 'u' : unexport = 1 ; break ;
       case 'D' : defaultval = localopt.arg ; break ;
+      case 'N' : si.chomp = 0 ; break ;
       case 'n' : si.chomp = 1 ; break ;
       case 's' : si.split = 1 ; break ;
       case 'C' : si.crunch = 1 ; break ;
diff --git a/src/libexecline/exlsn_multidefine.c b/src/libexecline/exlsn_multidefine.c
index c28d926..38147e6 100644
--- a/src/libexecline/exlsn_multidefine.c
+++ b/src/libexecline/exlsn_multidefine.c
@@ -22,12 +22,13 @@ int exlsn_multidefine (int argc, char const **argv, char const *const *envp, exl
   si.split = 1 ;
   for (;;)
   {
-    int opt = subgetopt_r(argc, argv, "0rnCcd:", &localopt) ;
+    int opt = subgetopt_r(argc, argv, "0rNnCcd:", &localopt) ;
     if (opt < 0) break ;
     switch (opt)
     {
       case '0' : zeroword = 1 ; break ;
       case 'r' : likeread = 1 ; break ;
+      case 'N' : si.chomp = 0 ; break ;
       case 'n' : si.chomp = 1 ; break ;
       case 'C' : si.crunch = 1 ; break ;
       case 'c' : si.crunch = 0 ; break ;