Don't expand FOR variables until execution time, so that special characters in them won't cause unwanted syntactic effects.

For example, "for %a in (^>) do echo %a" should just echo the greater than sign.

svn path=/trunk/; revision=39611
This commit is contained in:
Jeffrey Morlan 2009-02-15 18:18:16 +00:00
parent a5d9170830
commit 9b0334da19
5 changed files with 64 additions and 24 deletions

View file

@ -405,19 +405,14 @@ LPTSTR ReadBatchLine ()
}
/* At this point, fv points to parameter string */
bc->forvalue = fv;
/* Double up % signs so they will get through the parser intact */
while (*sp)
{
if ((*sp == _T('%')) && (*(sp + 1) == bc->forvar))
{
/* replace % var */
dp = _stpcpy (dp, fv);
sp += 2;
}
else
{
/* Else just copy */
*dp++ = *sp++;
}
if (*sp == _T('%'))
*dp++ = _T('%');
*dp++ = *sp++;
}
*dp++ = _T('\n');

View file

@ -21,6 +21,7 @@ typedef struct tagBATCHCONTEXT
HANDLE hFind; /* Preserve find handle when doing a for */
REDIRECTION *RedirList;
TCHAR forvar;
LPTSTR forvalue;
} BATCH_CONTEXT, *LPBATCH_CONTEXT;

View file

@ -1261,19 +1261,56 @@ too_long:
#undef APPEND1
}
BOOL
SubstituteForVars(TCHAR *Src, TCHAR *Dest)
{
TCHAR *DestEnd = &Dest[CMDLINE_LENGTH - 1];
while (*Src)
{
if (Src[0] == _T('%') && Src[1] != _T('\0'))
{
/* This might be a variable. Search the list of contexts for it */
BATCH_CONTEXT *Ctx = bc;
while (Ctx && Ctx->forvar != Src[1])
Ctx = Ctx->prev;
if (Ctx)
{
/* Found it */
if (Dest + _tcslen(Ctx->forvalue) > DestEnd)
return FALSE;
Dest = _stpcpy(Dest, Ctx->forvalue);
Src += 2;
continue;
}
}
/* Not a variable; just copy the character */
if (Dest >= DestEnd)
return FALSE;
*Dest++ = *Src++;
}
*Dest = _T('\0');
return TRUE;
}
LPTSTR
DoDelayedExpansion(LPTSTR Line)
{
TCHAR Buf[CMDLINE_LENGTH];
if (!_tcschr(Line, _T('!')))
return cmd_dup(Line);
TCHAR Buf1[CMDLINE_LENGTH];
TCHAR Buf2[CMDLINE_LENGTH];
/* First, substitute FOR variables */
if (!SubstituteForVars(Line, Buf1))
return NULL;
if (!_tcschr(Buf1, _T('!')))
return cmd_dup(Buf1);
/* FIXME: Delayed substitutions actually aren't quite the same as
* immediate substitutions. In particular, it's possible to escape
* the exclamation point using ^. */
if (!SubstituteVars(Line, Buf, _T('!')))
if (!SubstituteVars(Buf1, Buf2, _T('!')))
return NULL;
return cmd_dup(Buf);
return cmd_dup(Buf2);
}

View file

@ -102,6 +102,7 @@ BOOL ExecuteCommand(struct _PARSED_COMMAND *Cmd);
LPCTSTR GetEnvVarOrSpecial ( LPCTSTR varName );
VOID AddBreakHandler (VOID);
VOID RemoveBreakHandler (VOID);
BOOL SubstituteForVars(TCHAR *Src, TCHAR *Dest);
LPTSTR DoDelayedExpansion(LPTSTR Line);
BOOL DoCommand (LPTSTR line);
BOOL ReadLine(TCHAR *commandline, BOOL bMore);

View file

@ -610,18 +610,21 @@ ParseCommand(LPTSTR Line)
return Cmd;
}
/* Reconstruct a parse tree into text form;
* used for echoing batch file commands */
/* Reconstruct a parse tree into text form; used for echoing
* batch file commands and FOR instances. */
VOID
EchoCommand(PARSED_COMMAND *Cmd)
{
TCHAR Buf[CMDLINE_LENGTH];
PARSED_COMMAND *Sub;
REDIRECTION *Redir;
switch (Cmd->Type)
{
case C_COMMAND:
ConOutPrintf(_T("%s"), Cmd->Command.CommandLine);
if (SubstituteForVars(Cmd->Command.CommandLine, Buf))
ConOutPrintf(_T("%s"), Buf);
break;
case C_QUIET:
return;
@ -649,9 +652,11 @@ EchoCommand(PARSED_COMMAND *Cmd)
ConOutPrintf(_T(" /I"));
if (Cmd->If.Flags & IFFLAG_NEGATE)
ConOutPrintf(_T(" not"));
if (Cmd->If.LeftArg)
ConOutPrintf(_T(" %s"), Cmd->If.LeftArg);
ConOutPrintf(_T(" %s %s "), IfOperatorString[Cmd->If.Operator], Cmd->If.RightArg);
if (Cmd->If.LeftArg && SubstituteForVars(Cmd->If.LeftArg, Buf))
ConOutPrintf(_T(" %s"), Buf);
ConOutPrintf(_T(" %s"), IfOperatorString[Cmd->If.Operator]);
if (SubstituteForVars(Cmd->If.RightArg, Buf))
ConOutPrintf(_T(" %s "), Buf);
Sub = Cmd->Subcommands;
EchoCommand(Sub);
if (Sub->Next)
@ -664,8 +669,9 @@ EchoCommand(PARSED_COMMAND *Cmd)
for (Redir = Cmd->Redirections; Redir; Redir = Redir->Next)
{
ConOutPrintf(_T(" %c%s%s"), _T('0') + Redir->Number,
RedirString[Redir->Type], Redir->Filename);
if (SubstituteForVars(Redir->Filename, Buf))
ConOutPrintf(_T(" %c%s%s"), _T('0') + Redir->Number,
RedirString[Redir->Type], Buf);
}
}