Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

History substitution fix



The patch below fixes a recently reported by
Mark Hanson <mark@xxxxxxxxxxxx>:

% echo $!
0
% echo !$

causes an infinite loop.  Please apply this patch and test history
substitutions with various history events containing a bang.

After this patch no additional backslashes will be added if a bang comes
from a history substitution.  This means that in

% echo !
!
% echo !$

the !$ expands to ! instead of \!.

The code than handles bangs in hgetc and hungetc is quite sort but a bit
difficult to understand but there are some long comments which are trying
to explain the situation.

Zoltan


*** Src/hist.c	1996/06/26 23:19:48	2.11
--- Src/hist.c	1996/06/27 21:03:08
***************
*** 118,134 ****
  	    return ' ';
  	}
      }
!     if (alstackind == 1 && !alstack[0] && !stophist &&
! 	c == '\\' && (c = ingetc()) != bangchar)
  	/* the current character c came from a history expansion        *
  	 * (alstackind == 1 && !alstack[0]) and history is not disabled *
! 	 * (e.g. we are not inside qingle quotes). In that case, \!     *
  	 * should be treated as ! (since this \! came from a previous   *
  	 * history line where \ was used to escape the bang). So if     *
  	 * c == '\\' we fetch one more character to see if it's a bang, *
  	 * and if it is not, we unget it and reset c back to '\\'       */
! 	safeinungetc(c), c = '\\';
!     if (stophist || alstackind)
  	/* If the result is a bangchar which came from history or alias  *
  	 * expansion, we treat it as an escaped bangchar, unless history *
  	 * is disabled. If stophist == 1 it only means that history is   *
--- 118,135 ----
  	    return ' ';
  	}
      }
!     if (alstackind == 1 && !alstack[0] && !stophist) {
  	/* the current character c came from a history expansion        *
  	 * (alstackind == 1 && !alstack[0]) and history is not disabled *
! 	 * (e.g. we are not inside single quotes). In that case, \!     *
  	 * should be treated as ! (since this \! came from a previous   *
  	 * history line where \ was used to escape the bang). So if     *
  	 * c == '\\' we fetch one more character to see if it's a bang, *
  	 * and if it is not, we unget it and reset c back to '\\'       */
! 	qbang = 0;
! 	if (c == '\\' && !(qbang = (c = ingetc()) == bangchar))
! 	    safeinungetc(c), c = '\\';
!     } else if (stophist || alstackind)
  	/* If the result is a bangchar which came from history or alias  *
  	 * expansion, we treat it as an escaped bangchar, unless history *
  	 * is disabled. If stophist == 1 it only means that history is   *
***************
*** 535,542 ****
  	}
  #endif
      }
-     /* I have to look at this part more closely to understand what Peter   *
-      * did here (Zoltan)  #######                                          */
      if (doit)
  	inungetc(c);
      if (c == bangchar && stophist < 2 && qbang) {
--- 536,541 ----
***************
*** 552,559 ****
  	 * bangchar was quoted. In some rare cases when more than one      *
  	 * bangs are unget by the lexer, this may give a wrong result, but *
  	 * it can happen only if bangchar is some very unusual character   *
! 	 * (unusual means unusual for a bang like a number).               */
! 	doit = !stophist;
  	qbang = 0;
  	c = '\\';
  	goto escape;
--- 551,560 ----
  	 * bangchar was quoted. In some rare cases when more than one      *
  	 * bangs are unget by the lexer, this may give a wrong result, but *
  	 * it can happen only if bangchar is some very unusual character   *
! 	 * (unusual means unusual for a bang like a number).  If this bang *
! 	 * comes from an alias expansion, no backslash should be really    *
! 	 * unget.                                                          */
! 	doit = !stophist && (!alstackind || (alstackind == 1 && !alstack[0]));
  	qbang = 0;
  	c = '\\';
  	goto escape;




Messages sorted by: Reverse Date, Date, Thread, Author