From owner-freebsd-bugs@FreeBSD.ORG Mon Mar 21 01:20:07 2005 Return-Path: Delivered-To: freebsd-bugs@hub.freebsd.org Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id 5F21316A4CE for ; Mon, 21 Mar 2005 01:20:07 +0000 (GMT) Received: from freefall.freebsd.org (freefall.freebsd.org [216.136.204.21]) by mx1.FreeBSD.org (Postfix) with ESMTP id 1E04343D46 for ; Mon, 21 Mar 2005 01:20:07 +0000 (GMT) (envelope-from gnats@FreeBSD.org) Received: from freefall.freebsd.org (gnats@localhost [127.0.0.1]) by freefall.freebsd.org (8.13.3/8.13.3) with ESMTP id j2L1K6Sj092105 for ; Mon, 21 Mar 2005 01:20:06 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.13.3/8.13.1/Submit) id j2L1K60w092104; Mon, 21 Mar 2005 01:20:06 GMT (envelope-from gnats) Resent-Date: Mon, 21 Mar 2005 01:20:06 GMT Resent-Message-Id: <200503210120.j2L1K60w092104@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Vsevolod Stakhov Received: from mx1.FreeBSD.org (mx1.freebsd.org [216.136.204.125]) by hub.freebsd.org (Postfix) with ESMTP id E881916A4CE for ; Mon, 21 Mar 2005 01:18:27 +0000 (GMT) Received: from spray.anyhost.ru (spray.anyhost.ru [213.219.249.64]) by mx1.FreeBSD.org (Postfix) with ESMTP id 753F743D46 for ; Mon, 21 Mar 2005 01:18:27 +0000 (GMT) (envelope-from vsevolod@highsecure.ru) Received: from cebka by spray.anyhost.ru with local (Exim 4.50 (FreeBSD)) id 1DDBYo-0001fQ-90 for FreeBSD-gnats-submit@freebsd.org; Mon, 21 Mar 2005 04:18:26 +0300 Message-Id: Date: Mon, 21 Mar 2005 04:18:26 +0300 From: Vsevolod Stakhov Sender: "Vsevolod A. Stakhov" To: FreeBSD-gnats-submit@FreeBSD.org X-Send-Pr-Version: 3.113 Subject: bin/79067: /bin/sh should be more intelligent about IFS X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.1 Precedence: list Reply-To: Vsevolod Stakhov List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 21 Mar 2005 01:20:07 -0000 >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: