about summary refs log tree commit diff
path: root/Src/hist.c
diff options
context:
space:
mode:
authorPeter Stephenson <pws@users.sourceforge.net>2011-07-12 08:37:11 +0000
committerPeter Stephenson <pws@users.sourceforge.net>2011-07-12 08:37:11 +0000
commita05a51ce8a9ea46b6c019cd2bab39a405256a07f (patch)
tree94a2ff614e042c24f0746b25c8b87ff991ffda19 /Src/hist.c
parentd1a557d008b7fa7881327acbd6decdb50631cc9c (diff)
downloadzsh-a05a51ce8a9ea46b6c019cd2bab39a405256a07f.tar.gz
zsh-a05a51ce8a9ea46b6c019cd2bab39a405256a07f.tar.xz
zsh-a05a51ce8a9ea46b6c019cd2bab39a405256a07f.zip
29542: fix crash in hbegin(), remove bad test
29543: fix backslash-newline within words with histlexwords
Diffstat (limited to 'Src/hist.c')
-rw-r--r--Src/hist.c150
1 files changed, 113 insertions, 37 deletions
diff --git a/Src/hist.c b/Src/hist.c
index 87bfde882..f1bae14f4 100644
--- a/Src/hist.c
+++ b/Src/hist.c
@@ -876,7 +876,18 @@ hbegin(int dohist)
 	stophist = (!interact || unset(SHINSTDIN)) ? 2 : 0;
     else
 	stophist = 0;
-    if (stophist == 2 || (inbufflags & INP_ALIAS)) {
+    /*
+     * pws: We used to test for "|| (inbufflags & INP_ALIAS)"
+     * in this test, but at this point we don't have input
+     * set up up so this can trigger unnecessarily.
+     * I don't see how the test at this point could ever be
+     * useful, since we only get here when we're initialising
+     * the history mechanism, before we've done any input.
+     *
+     * (I also don't see any point where this function is called with
+     * dohist=0.)
+     */
+    if (stophist == 2) {
 	chline = hptr = NULL;
 	hlinesz = 0;
 	chwords = NULL;
@@ -3090,7 +3101,10 @@ histsplitwords(char *lineptr, short **wordsp, int *nwordsp, int *nwordposp,
 	     wordnode;
 	     incnode(wordnode)) {
 	    char *word = getdata(wordnode);
+	    char *lptr, *wptr = word;
+	    int loop_next = 0, skipping;
 
+	    /* Skip stuff at the start of the word */
 	    for (;;) {
 		/*
 		 * Not really an oddity: "\\\n" is
@@ -3098,57 +3112,119 @@ histsplitwords(char *lineptr, short **wordsp, int *nwordsp, int *nwordposp,
 		 */
 		if (inblank(*lineptr))
 		    lineptr++;
-		else if (lineptr[0] == '\\' && lineptr[1] == '\n')
+		else if (lineptr[0] == '\\' && lineptr[1] == '\n') {
+		    /*
+		     * Optimisation: we handle this in the loop below,
+		     * too.
+		     */
 		    lineptr += 2;
-		else
+		} else
 		    break;
 	    }
-	    if (!strpfx(word, lineptr)) {
-		int bad = 0;
-		/*
-		 * Oddity 1: newlines turn into semicolons.
-		 */
-		if (!strcmp(word, ";"))
-		    continue;
-		while (*lineptr) {
-		    if (!*word) {
-			bad = 1;
+	    lptr = lineptr;
+	    /*
+	     * Skip chunks of word with possible intervening
+	     * backslash-newline.
+	     *
+	     * To get round C's annoying lack of ability to
+	     * reference the outer loop, we'll break from this
+	     * one with
+	     * loop_next = 0: carry on as normal
+	     * loop_next = 1: break from outer loop
+	     * loop_next = 2: continue round outer loop.
+	     */
+	    do {
+		skipping = 0;
+		if (strpfx(wptr, lptr)) {
+		    /*
+		     * Normal case: word from lexer matches start of
+		     * string from line.  Just advance over it.
+		     */
+		    int len;
+		    if (!strcmp(wptr, ";") && strpfx(";;", lptr)) {
+			/*
+			 * Don't get confused between a semicolon that's
+			 * probably really a newline and a double
+			 * semicolon that's terminating a case.
+			 */
+			loop_next = 2;
 			break;
 		    }
+		    len = strlen(wptr);
+		    lptr += len;
+		    wptr += len;
+		} else {
 		    /*
-		     * Oddity 2: !'s turn into |'s.
+		     * Didn't get to the end of the word.
+		     * See what's amiss.
 		     */
-		    if (*lineptr == *word ||
-			(*lineptr == '!' && *word == '|')) {
-			lineptr++;
-			word++;
-		    } else {
-			bad = 1;
+		    int bad = 0;
+		    /*
+		     * Oddity 1: newlines turn into semicolons.
+		     */
+		    if (!strcmp(wptr, ";"))
+		    {
+			loop_next = 2;
 			break;
 		    }
-		}
-		if (bad) {
+		    while (*lptr) {
+			if (!*wptr) {
+			    /*
+			     * End of the word before the end of the
+			     * line: not good.
+			     */
+			    bad = 1;
+			    loop_next = 1;
+			    break;
+			}
+			/*
+			 * Oddity 2: !'s turn into |'s.
+			 */
+			if (*lptr == *wptr ||
+			    (*lptr == '!' && *wptr == '|')) {
+			    lptr++;
+			    wptr++;
+			} else if (lptr[0] == '\\' &&
+				   lptr[1] == '\n') {
+			    /*
+			     * \\\n can occur in the middle of a word;
+			     * wptr is already pointing at this, we
+			     * just need to skip over the break
+			     * in lptr and look at the next chunk.
+			     */
+			    lptr += 2;
+			    skipping = 1;
+			    break;
+			} else {
+			    bad = 1;
+			    loop_next = 1;
+			    break;
+			}
+		    }
+		    if (bad) {
 #ifdef DEBUG
-		    dputs(ERRMSG("bad wordsplit reading history: "
-				 "%s\nat: %s\nword: %s"),
-			  start, lineptr, word);
+			dputs(ERRMSG("bad wordsplit reading history: "
+				     "%s\nat: %s\nword: %s"),
+			      start, lineptr, word);
 #endif
-		    lineptr = start;
-		    nwordpos = 0;
-		    uselex = 0;
-		    break;
+			lineptr = start;
+			nwordpos = 0;
+			uselex = 0;
+			loop_next = 1;
+		    }
 		}
-	    } else if (!strcmp(word, ";") && strpfx(";;", lineptr)) {
-		/*
-		 * Don't get confused between a semicolon that's
-		 * probably really a newline and a double
-		 * semicolon that's terminating a case.
-		 */
+	    } while (skipping);
+	    if (loop_next) {
+		if (loop_next == 1)
+		    break;
 		continue;
 	    }
+	    /* Record position of current word... */
 	    words[nwordpos++] = lineptr - start;
-	    lineptr += strlen(word);
-	    words[nwordpos++] = lineptr - start;
+	    words[nwordpos++] = lptr - start;
+
+	    /* ready for start of next word. */
+	    lineptr = lptr;
 	}
     }
     if (!uselex) {