tags 607174 + upstream patch fixed-upstream
quit

Cristian Ionescu-Idbohrn wrote:

> IFS=': '
> ip -o link show | while read n i s rest; do
>       echo n="|$n|", i="|$i|", s="|$s|", rest="|$rest|" >&2
> done
> 
> The unexpected output from the above is:
> 
> n=|2|, i=||, s=|eth0|,
[...]
> After swapping the IFS characters (IFS=' :'), I get the expected output:
> 
> n=|2|, i=|eth0|, s=|<BROADCAST,MULTICAST,UP,LOWER_UP>|,
[...]

Thanks for reporting.  How about this?

-- 8< --
From: Stefan Potyra <stefan.pot...@informatik.uni-erlangen.de>
Date: Tue, 11 Aug 2009 12:46:26 +1000
Subject: [PATCH 1/2] [BUILTIN] Honor tab as IFS whitespace when splitting 
fields in readcmd

When I try to split fields by tabs, dash doesn't honour multiple tabs
between fields as whitespace (at least that's how I interpret [1],
please correct me if I'm wrong).

TESTSTRING="1   2               3"

IFS="   "
echo "$TESTSTRING" | while read p1 p2 p3; do
        echo "p1=${p1}, p2=${p2}, p3=${p3}"
done

Signed-off-by: Herbert Xu <herb...@gondor.apana.org.au>
(cherry picked from commit 55c46b7286f5d9f2d8291158203e2b61d2494420)
Signed-off-by: Jonathan Nieder <jrnie...@gmail.com>
---
 ChangeLog       |    4 ++
 src/expand.c    |    9 ++---
 src/expand.h    |    3 ++
 src/miscbltin.c |   97 ++++++++++++++++++++++++++++++++++++++----------------
 4 files changed, 78 insertions(+), 35 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 1d18cbf..4cde9e6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2009-08-11  Stefan Potyra <stefan.pot...@informatik.uni-erlangen.de>
+
+       * Honor tab as IFS whitespace when splitting fields in readcmd.
+
 2009-01-14  Herbert Xu <herb...@gondor.apana.org.au>
 
        * Add arith_yacc.h to dash_SOURCES.
diff --git a/src/expand.c b/src/expand.c
index 7995d40..48c45e5 100644
--- a/src/expand.c
+++ b/src/expand.c
@@ -117,9 +117,6 @@ STATIC char *evalvar(char *, int);
 STATIC size_t strtodest(const char *, const char *, int);
 STATIC void memtodest(const char *, size_t, const char *, int);
 STATIC ssize_t varvalue(char *, int, int);
-STATIC void recordregion(int, int, int);
-STATIC void removerecordregions(int); 
-STATIC void ifsbreakup(char *, struct arglist *);
 STATIC void ifsfree(void);
 STATIC void expandmeta(struct strlist *, int);
 #ifdef HAVE_GLOB
@@ -412,7 +409,7 @@ lose:
 }
 
 
-STATIC void 
+void 
 removerecordregions(int endoff)
 {
        if (ifslastp == NULL)
@@ -1001,7 +998,7 @@ value:
  * string for IFS characters.
  */
 
-STATIC void
+void
 recordregion(int start, int end, int nulonly)
 {
        struct ifsregion *ifsp;
@@ -1028,7 +1025,7 @@ recordregion(int start, int end, int nulonly)
  * strings to the argument list.  The regions of the string to be
  * searched for IFS characters have been stored by recordregion.
  */
-STATIC void
+void
 ifsbreakup(char *string, struct arglist *arglist)
 {
        struct ifsregion *ifsp;
diff --git a/src/expand.h b/src/expand.h
index 1862aea..405af0b 100644
--- a/src/expand.h
+++ b/src/expand.h
@@ -67,6 +67,9 @@ void expari(int);
 #define rmescapes(p) _rmescapes((p), 0)
 char *_rmescapes(char *, int);
 int casematch(union node *, char *);
+void recordregion(int, int, int);
+void removerecordregions(int); 
+void ifsbreakup(char *, struct arglist *);
 
 /* From arith.y */
 intmax_t arith(const char *);
diff --git a/src/miscbltin.c b/src/miscbltin.c
index 3f91bc3..cca0f6c 100644
--- a/src/miscbltin.c
+++ b/src/miscbltin.c
@@ -55,14 +55,73 @@
 #include "miscbltin.h"
 #include "mystring.h"
 #include "main.h"
+#include "expand.h"
+#include "parser.h"
 
 #undef rflag
 
 
+/** handle one line of the read command.
+ *  more fields than variables -> remainder shall be part of last variable.
+ *  less fields than variables -> remaining variables unset.
+ *
+ *  @param line complete line of input
+ *  @param ap argument (variable) list
+ *  @param len length of line including trailing '\0'
+ */
+static void
+readcmd_handle_line(char *line, char **ap, size_t len)
+{
+       struct arglist arglist;
+       struct strlist *sl;
+       char *s, *backup;
+
+       /* ifsbreakup will fiddle with stack region... */
+       s = grabstackstr(line + len);
+
+       /* need a copy, so that delimiters aren't lost
+        * in case there are more fields than variables */
+       backup = sstrdup(line);
+
+       arglist.lastp = &arglist.list;
+       recordregion(0, len, 0);
+       
+       ifsbreakup(s, &arglist);
+       *arglist.lastp = NULL;
+       removerecordregions(0);
+
+       for (sl = arglist.list; sl; sl = sl->next) {
+               /* remaining fields present, but no variables left. */
+               if (!ap[1]) {
+                       size_t offset;
+                       char *remainder;
+
+                       /* FIXME little bit hacky, assuming that ifsbreakup 
+                        * will not modify the length of the string */
+                       offset = sl->text - s;
+                       remainder = backup + offset;
+                       rmescapes(remainder);
+                       setvar(*ap, remainder, 0);
+
+                       return;
+               }
+               
+               /* set variable to field */
+               rmescapes(sl->text);
+               setvar(*ap, sl->text, 0);
+               ap++;
+       }
+
+       /* nullify remaining arguments */
+       do {
+               setvar(*ap, nullstr, 0);
+       } while (*++ap);
+}
 
 /*
  * The read builtin.  The -e option causes backslashes to escape the
- * following character.
+ * following character. The -p option followed by an argument prompts
+ * with the argument.
  *
  * This uses unbuffered input, which may be avoidable in some cases.
  */
@@ -75,9 +134,7 @@ readcmd(int argc, char **argv)
        char c;
        int rflag;
        char *prompt;
-       const char *ifs;
        char *p;
-       int startword;
        int status;
        int i;
 
@@ -97,10 +154,7 @@ readcmd(int argc, char **argv)
        }
        if (*(ap = argptr) == NULL)
                sh_error("arg count");
-       if ((ifs = bltinlookup("IFS")) == NULL)
-               ifs = defifs;
        status = 0;
-       startword = 1;
        backslash = 0;
        STARTSTACKSTR(p);
        for (;;) {
@@ -111,10 +165,10 @@ readcmd(int argc, char **argv)
                if (c == '\0')
                        continue;
                if (backslash) {
-                       backslash = 0;
-                       if (c != '\n')
-                               goto put;
-                       continue;
+                       if (c == '\n')
+                               goto resetbs;
+                       STPUTC(CTLESC, p);
+                       goto put;
                }
                if (!rflag && c == '\\') {
                        backslash++;
@@ -122,28 +176,13 @@ readcmd(int argc, char **argv)
                }
                if (c == '\n')
                        break;
-               if (startword && *ifs == ' ' && strchr(ifs, c)) {
-                       continue;
-               }
-               startword = 0;
-               if (ap[1] != NULL && strchr(ifs, c) != NULL) {
-                       STACKSTRNUL(p);
-                       setvar(*ap, stackblock(), 0);
-                       ap++;
-                       startword = 1;
-                       STARTSTACKSTR(p);
-               } else {
 put:
-                       STPUTC(c, p);
-               }
+               STPUTC(c, p);
+resetbs:
+               backslash = 0;
        }
        STACKSTRNUL(p);
-       /* Remove trailing blanks */
-       while ((char *)stackblock() <= --p && strchr(ifs, *p) != NULL)
-               *p = '\0';
-       setvar(*ap, stackblock(), 0);
-       while (*++ap != NULL)
-               setvar(*ap, nullstr, 0);
+       readcmd_handle_line(stackblock(), ap, p - (char *)stackblock());
        return status;
 }
 

From: Herbert Xu <herb...@gondor.apana.org.au>
Date: Mon, 31 Aug 2009 20:15:04 +1000
Subject: [PATCH 2/2] [BUILTIN] Fix NUL termination in readcmd

Commit 55c46b7286f5d9f2d8291158203e2b61d2494420 ([BUILTIN] Honor
tab as IFS whitespace when splitting fields in readcmd) introduced
a bug where sometimes garbage would follow the last field preceding
the end-of-line.  This was caused by an off-by-one error in the
string length calculation.

This patch fixes the bug.

Signed-off-by: Herbert Xu <herb...@gondor.apana.org.au>
(cherry picked from commit cdb7c72699056856ac123d1ba4ab0e8595edbeb9)
Signed-off-by: Jonathan Nieder <jrnie...@gmail.com>

 ChangeLog       |    4 ++++
 src/miscbltin.c |    2 +-
 2 files changed, 5 insertions(+), 1 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 4cde9e6..c6bf614 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2009-08-31  Herbert Xu <herb...@gondor.apana.org.au>
+
+       * Fix NUL termination in readcmd.
+
 2009-08-11  Stefan Potyra <stefan.pot...@informatik.uni-erlangen.de>
 
        * Honor tab as IFS whitespace when splitting fields in readcmd.
diff --git a/src/miscbltin.c b/src/miscbltin.c
index cca0f6c..be746b2 100644
--- a/src/miscbltin.c
+++ b/src/miscbltin.c
@@ -182,7 +182,7 @@ resetbs:
                backslash = 0;
        }
        STACKSTRNUL(p);
-       readcmd_handle_line(stackblock(), ap, p - (char *)stackblock());
+       readcmd_handle_line(stackblock(), ap, p + 1 - (char *)stackblock());
        return status;
 }
 
-- 
1.7.3.3.573.g0bcfc




-- 
To UNSUBSCRIBE, email to debian-bugs-dist-requ...@lists.debian.org
with a subject of "unsubscribe". Trouble? Contact listmas...@lists.debian.org

Reply via email to