summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog11
-rw-r--r--Src/Zle/zle_main.c2
-rw-r--r--Src/hist.c150
3 files changed, 124 insertions, 39 deletions
diff --git a/ChangeLog b/ChangeLog
index e2fa793ca..20fa306e7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,12 @@
+2011-07-12 Peter Stephenson <pws@csr.com>
+
+ * 29543: Src/hist.c: saved history lines with backslash-newline
+ in the middle of words confused histlexwords.
+
+ * 29542: Src/hist.c, Src/Zle/zle_main.c: remove test when
+ initialising history that could cause crashes (and was probably
+ never useful); ensure ZLE returns NULL if there's an error.
+
2011-07-04 Peter Stephenson <p.w.stephenson@ntlworld.com>
* Eric Moors: 29531: Completion/Unix/Command/_adb: completion
@@ -15097,5 +15106,5 @@
*****************************************************
* This is used by the shell to define $ZSH_PATCHLEVEL
-* $Revision: 1.5394 $
+* $Revision: 1.5395 $
*****************************************************
diff --git a/Src/Zle/zle_main.c b/Src/Zle/zle_main.c
index 6acedee70..771ff2cf0 100644
--- a/Src/Zle/zle_main.c
+++ b/Src/Zle/zle_main.c
@@ -1233,7 +1233,7 @@ zleread(char **lp, char **rp, int flags, int context)
alarm(0);
freeundo();
- if (eofsent) {
+ if (eofsent || errflag) {
s = NULL;
} else {
zleline[zlell++] = ZWC('\n');
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) {