bin/79067: /bin/sh should be more intelligent about IFS
Vsevolod Stakhov
vsevolod at highsecure.ru
Sun Mar 20 17:20:07 PST 2005
>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 at 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:
More information about the freebsd-bugs
mailing list