From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 15598 invoked by alias); 8 Oct 2013 02:56:25 -0000 Mailing-List: contact zsh-workers-help@zsh.org; run by ezmlm Precedence: bulk X-No-Archive: yes List-Id: Zsh Workers List List-Post: List-Help: X-Seq: 31797 Received: (qmail 23192 invoked from network); 8 Oct 2013 02:56:18 -0000 X-Spam-Checker-Version: SpamAssassin 3.3.2 (2011-06-06) on f.primenet.com.au X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,RCVD_IN_DNSWL_NONE autolearn=ham version=3.3.2 From: Bart Schaefer Message-id: <131007195612.ZM1029@torch.brasslantern.com> Date: Mon, 07 Oct 2013 19:56:12 -0700 In-reply-to: Comments: In reply to Hauke Petersen "Inconsistent history expansion of characters adjacent to histchar" (Oct 7, 5:55pm) References: X-Mailer: OpenZMail Classic (0.9.2 24April2005) To: zsh-workers@zsh.org Subject: Re: Inconsistent history expansion of characters adjacent to histchar MIME-version: 1.0 Content-type: text/plain; charset=us-ascii On Mon, Oct 7, 2013 at 8:55 AM, Hauke Petersen wrote: > It does not immediately make sense to me why a single `!' would be > interpreted as a double `!!' before a `;' and tried, unsuccessfully, > to find an explanation. Bluntly, it's because that history documentation is lying to you. The correct bit is the first sentence here: > Event Designators > ! Start a history expansion, except when followed by a blank, new-line, > `=' or `('. If followed immediately by a word designator, this forms a > history reference with no event designator. Semicolon is not one of the things that prevents a "!" from starting a history expansion, so everything from there forward behaves like a history reference with no event. This bug appears to have been around for longer than we've been keeping source control copies of the shell. For some reason ';' is called out in histsubchar() along with things that begin (or end) event or word designators. The same is true of any kind of quotes, e.g.: print !`echo foo` print !!`echo foo` are equivalent; "!}" also has the same problem though it usually causes a parse error. I think the intent is that those characters only end a history reference after at least one other character [not in that set] has been read, but that's not the way the loop is written, and it doesn't account for other characters that can't be word designators, as seen here: > there is no previous command starting with ';'. Like otherwise > similar `&': > > % print !& print foo > zsh: event not found: & Same happens with "!|" and probably a few other obscure variations. In short, the history parser has always been very ad-hoc and deviates from its own spec in several ways. Here's a patch that fixes the unintended expansion, but it doesn't fix the inconsistent behavior with !& !| et al. There should probably be more method to this madness instead of multiple series of individual character comparisons. The (c == '"') condition is probably unnecessary because '!"' has been handled much earlier, but the loop above this also tests all three quotes, so ... diff --git a/Src/hist.c b/Src/hist.c index d1af30a..bd650e8 100644 --- a/Src/hist.c +++ b/Src/hist.c @@ -521,6 +521,12 @@ histsubchar(int c) } c = ingetc(); } + if (ptr == buf && + (c == '}' || c == ';' || c == '\'' || c == '"' || c == '`')) { + /* Neither event nor word designator, no expansion */ + safeinungetc(c); + return bangchar; + } *ptr = 0; if (!*buf) { if (c != '%') {