Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 21 Mar 2005 04:18:26 +0300
From:      Vsevolod Stakhov <vsevolod@highsecure.ru>
To:        FreeBSD-gnats-submit@FreeBSD.org
Subject:   bin/79067: /bin/sh should be more intelligent about IFS
Message-ID:  <E1DDBYo-0001fQ-90@spray.anyhost.ru>
Resent-Message-ID: <200503210120.j2L1K60w092104@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         79067
>Category:       bin
>Synopsis:       /bin/sh should be more intelligent about IFS
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          change-request
>Submitter-Id:   current-users
>Arrival-Date:   Mon Mar 21 01:20:06 GMT 2005
>Closed-Date:
>Last-Modified:
>Originator:     Vsevolod Stakhov
>Release:        FreeBSD 5.3-STABLE i386
>Organization:
>Environment:
System: FreeBSD nemezida.highsecure.ru 5.3-STABLE FreeBSD 5.3-STABLE #2: Fri Jan 28 02:15:40 MSK 2005 root@nemezida.highsecure.ru:/mnt/data/usr/obj/mnt/data/usr/src/sys/NK i386

>Description:
/bin/sh in FreeBSD sometimes does not confirm POSIX. NetBSD people (David Laight)
has changed sh read built-in and expand.c. So I have adapted their changes to
FreeBSD. Now /bin/sh passes test from http://www.research.att.com/~gsf/public/ifs.sh

>How-To-Repeat:
>Fix:

--- /usr/src/bin/sh/miscbltin.c	Wed Jan 26 21:28:53 2005
+++ src/bin/sh/miscbltin.c	Mon Mar 21 03:47:51 2005
@@ -65,8 +65,8 @@
 #undef eflag
 
 /*
- * The read builtin.  The -r option causes backslashes to be treated like
- * ordinary characters.
+ * The read builtin.
+ * Backslahes escape the next char unless -r is specified.
  *
  * This uses unbuffered input, which may be avoidable in some cases.
  */
@@ -75,15 +75,16 @@
 readcmd(int argc __unused, char **argv __unused)
 {
 	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;
 	struct timeval tv;
 	char *tvptr;
 	fd_set ifds;
@@ -131,7 +132,7 @@
 	if (*(ap = argptr) == NULL)
 		error("arg count");
 	if ((ifs = bltinlookup("IFS", 1)) == NULL)
-		ifs = nullstr;
+		ifs = " \t\n";
 
 	if (tv.tv_sec >= 0) {
 		/*
@@ -162,8 +163,8 @@
 	}
 
 	status = 0;
-	startword = 1;
-	backslash = 0;
+	startword = 2;
+	
 	STARTSTACKSTR(p);
 	for (;;) {
 		if (read(STDIN_FILENO, &c, 1) != 1) {
@@ -172,40 +173,83 @@
 		}
 		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)) {
-			continue;
-		}
-		startword = 0;
-		if (backslash && c == '\\') {
-			if (read(STDIN_FILENO, &c, 1) != 1) {
-				status = 1;
-				break;
+		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 period */
+				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);
-		} else if (ap[1] != NULL && strchr(ifs, c) != NULL) {
-			STACKSTRNUL(p);
-			setvar(*ap, stackblock(), 0);
-			ap++;
-			startword = 1;
-			STARTSTACKSTR(p);
-		} else {
+			continue;
+		}
+		
+		/* End of variable */
+		startword = is_ifs;
+		if (ap[1] == NULL) {
+			/* Last variable needs all IFS chars */
+			saveall ++;
 			STPUTC(c, p);
+			continue;
 		}
+
+		STACKSTRNUL(p);
+		setvar(*ap, stackblock(), 0);
+		ap++;
+		STARTSTACKSTR(p);
 	}
 	STACKSTRNUL(p);
+	/* 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 remain args to "" */
 	while (*++ap != NULL)
 		setvar(*ap, nullstr, 0);
 	return status;

--- /usr/src/bin/sh/expand.c	Wed Jan 26 21:28:53 2005
+++ src/bin/sh/expand.c	Mon Mar 21 04:07:00 2005
@@ -1022,8 +1022,8 @@
 					p++;
 			}
 		} while ((ifsp = ifsp->next) != NULL);
-		if (*start || (!ifsspc && start > string && 
-			(nulonly || 1))) {
+		if (*start /*|| (!ifsspc && start > string && 
+			(nulonly || 1))*/) {
 			sp = (struct strlist *)stalloc(sizeof *sp);
 			sp->text = start;
 			*arglist->lastp = sp;
>Release-Note:
>Audit-Trail:
>Unformatted:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?E1DDBYo-0001fQ-90>