Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/bin/sh Fix the way the 'read' builtin processes IFS. In par...



details:   https://anonhg.NetBSD.org/src/rev/965904ecdb13
branches:  trunk
changeset: 579608:965904ecdb13
user:      dsl <dsl%NetBSD.org@localhost>
date:      Sat Mar 19 14:22:50 2005 +0000

description:
Fix the way the 'read' builtin processes IFS.  In particular:
- IFS whitespace is now processes correctly,
- Trailing non-whitespace IFS characters are added to the last variable
  iff a subsequent variable would have been assigned a non-null string.
Now passes the 'read' tests in http://www.research.att.com/~gsf/public/ifs.sh

diffstat:

 bin/sh/miscbltin.c |  116 +++++++++++++++++++++++++++++++++++++---------------
 1 files changed, 83 insertions(+), 33 deletions(-)

diffs (187 lines):

diff -r 1c3d05eff441 -r 965904ecdb13 bin/sh/miscbltin.c
--- a/bin/sh/miscbltin.c        Sat Mar 19 13:00:55 2005 +0000
+++ b/bin/sh/miscbltin.c        Sat Mar 19 14:22:50 2005 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: miscbltin.c,v 1.34 2004/04/19 01:36:32 lukem Exp $     */
+/*     $NetBSD: miscbltin.c,v 1.35 2005/03/19 14:22:50 dsl Exp $       */
 
 /*-
  * Copyright (c) 1991, 1993
@@ -37,7 +37,7 @@
 #if 0
 static char sccsid[] = "@(#)miscbltin.c        8.4 (Berkeley) 5/4/95";
 #else
-__RCSID("$NetBSD: miscbltin.c,v 1.34 2004/04/19 01:36:32 lukem Exp $");
+__RCSID("$NetBSD: miscbltin.c,v 1.35 2005/03/19 14:22:50 dsl Exp $");
 #endif
 #endif /* not lint */
 
@@ -69,25 +69,36 @@
 
 
 /*
- * The read builtin.  The -e option causes backslashes to escape the
- * following character.
+ * The read builtin.
+ * Backslahes escape the next char unless -r is specified.
  *
  * This uses unbuffered input, which may be avoidable in some cases.
+ *
+ * Note that if IFS=' :' then read x y should work so that:
+ * 'a b'       x='a', y='b'
+ * ' a b '     x='a', y='b'
+ * ':b'                x='',  y='b'
+ * ':'         x='',  y=''
+ * '::'                x='',  y=''
+ * ': :'       x='',  y=''
+ * ':::'       x='',  y='::'
+ * ':b c:'     x='',  y='b c:'
  */
 
 int
 readcmd(int argc, char **argv)
 {
        char **ap;
-       int backslash;
        char c;
        int rflag;
        char *prompt;
-       char *ifs;
+       const char *ifs;
        char *p;
        int startword;
        int status;
        int i;
+       int is_ifs;
+       int saveall = 0;
 
        rflag = 0;
        prompt = NULL;
@@ -97,17 +108,20 @@
                else
                        rflag = 1;
        }
+
        if (prompt && isatty(0)) {
                out2str(prompt);
                flushall();
        }
+
        if (*(ap = argptr) == NULL)
                error("arg count");
+
        if ((ifs = bltinlookup("IFS", 1)) == NULL)
-               ifs = nullstr;
+               ifs = " \t\n";
+
        status = 0;
-       startword = 1;
-       backslash = 0;
+       startword = 2;
        STARTSTACKSTR(p);
        for (;;) {
                if (read(0, &c, 1) != 1) {
@@ -116,43 +130,79 @@
                }
                if (c == '\0')
                        continue;
-               if (backslash) {
-                       backslash = 0;
+               if (c == '\\' && !rflag) {
+                       if (read(0, &c, 1) != 1) {
+                               status = 1;
+                               break;
+                       }
                        if (c != '\n')
                                STPUTC(c, p);
                        continue;
                }
-               if (!rflag && c == '\\') {
-                       backslash++;
-                       continue;
-               }
                if (c == '\n')
                        break;
-               if (startword && *ifs == ' ' && strchr(ifs, c)) {
+               if (strchr(ifs, c))
+                       is_ifs = strchr(" \t\n", c) ? 1 : 2;
+               else
+                       is_ifs = 0;
+
+               if (startword != 0) {
+                       if (is_ifs == 1) {
+                               /* Ignore leading IFS whitespace */
+                               if (saveall)
+                                       STPUTC(c, p);
+                               continue;
+                       }
+                       if (is_ifs == 2 && startword == 1) {
+                               /* Only one non-whitespace IFS per word */
+                               startword = 2;
+                               if (saveall)
+                                       STPUTC(c, p);
+                               continue;
+                       }
+               }
+
+               if (is_ifs == 0) {
+                       /* append this character to the current variable */
+                       startword = 0;
+                       if (saveall)
+                               /* Not just a spare terminator */
+                               saveall++;
+                       STPUTC(c, p);
                        continue;
                }
-               startword = 0;
-               if (backslash && c == '\\') {
-                       if (read(0, &c, 1) != 1) {
-                               status = 1;
-                               break;
-                       }
+
+               /* end of variable... */
+               startword = is_ifs;
+
+               if (ap[1] == NULL) {
+                       /* Last variable needs all IFS chars */
+                       saveall++;
                        STPUTC(c, p);
-               } else if (ap[1] != NULL && strchr(ifs, c) != NULL) {
-                       STACKSTRNUL(p);
-                       setvar(*ap, stackblock(), 0);
-                       ap++;
-                       startword = 1;
-                       STARTSTACKSTR(p);
-               } else {
-                       STPUTC(c, p);
+                       continue;
                }
+
+               STACKSTRNUL(p);
+               setvar(*ap, stackblock(), 0);
+               ap++;
+               STARTSTACKSTR(p);
        }
        STACKSTRNUL(p);
-       /* Remove trailing blanks */
-       while (stackblock() <= --p && strchr(ifs, *p) != NULL)
-               *p = '\0';
+
+       /* Remove trailing IFS chars */
+       for (; stackblock() <= --p; *p = 0) {
+               if (!strchr(ifs, *p))
+                       break;
+               if (strchr(" \t\n", *p))
+                       /* Always remove whitespace */
+                       continue;
+               if (saveall > 1)
+                       /* Don't remove non-whitespace unless it was naked */
+                       break;
+       }
        setvar(*ap, stackblock(), 0);
+
+       /* Set any remaining args to "" */
        while (*++ap != NULL)
                setvar(*ap, nullstr, 0);
        return status;



Home | Main Index | Thread Index | Old Index