mirror of
https://github.com/reactos/reactos.git
synced 2025-02-22 16:36:33 +00:00
[CMD] Some fixes for getting the enhanced '%~XXX' batch/FOR variables.
CORE-11857 CORE-13736 It will be followed with a separate fix for the FOR-loop code. Fixes some cmd_winetests. A NULL pointer can be returned for a valid existing batch/FOR variable, in which case the enhanced-variable getter should return an empty string. This situation can happen e.g. when forcing a FOR-loop to tokenize a text line with not enough tokens in it.
This commit is contained in:
parent
014efdf7e8
commit
cb2a9c31a6
4 changed files with 146 additions and 58 deletions
|
@ -74,29 +74,35 @@ TCHAR textline[BATCH_BUFFSIZE];
|
||||||
/*
|
/*
|
||||||
* Returns a pointer to the n'th parameter of the current batch file.
|
* Returns a pointer to the n'th parameter of the current batch file.
|
||||||
* If no such parameter exists returns pointer to empty string.
|
* If no such parameter exists returns pointer to empty string.
|
||||||
* If no batch file is current, returns NULL
|
* If no batch file is current, returns NULL.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
LPTSTR FindArg(TCHAR Char, BOOL *IsParam0)
|
BOOL
|
||||||
|
FindArg(
|
||||||
|
IN TCHAR Char,
|
||||||
|
OUT PCTSTR* ArgPtr,
|
||||||
|
OUT BOOL* IsParam0)
|
||||||
{
|
{
|
||||||
LPTSTR pp;
|
PCTSTR pp;
|
||||||
INT n = Char - _T('0');
|
INT n = Char - _T('0');
|
||||||
|
|
||||||
TRACE ("FindArg: (%d)\n", n);
|
TRACE("FindArg: (%d)\n", n);
|
||||||
|
|
||||||
|
*ArgPtr = NULL;
|
||||||
|
|
||||||
if (n < 0 || n > 9)
|
if (n < 0 || n > 9)
|
||||||
return NULL;
|
return FALSE;
|
||||||
|
|
||||||
n = bc->shiftlevel[n];
|
n = bc->shiftlevel[n];
|
||||||
*IsParam0 = (n == 0);
|
*IsParam0 = (n == 0);
|
||||||
pp = bc->params;
|
pp = bc->params;
|
||||||
|
|
||||||
/* Step up the strings till we reach the end */
|
/* Step up the strings till we reach
|
||||||
/* or the one we want */
|
* the end or the one we want. */
|
||||||
while (*pp && n--)
|
while (*pp && n--)
|
||||||
pp += _tcslen (pp) + 1;
|
pp += _tcslen(pp) + 1;
|
||||||
|
|
||||||
return pp;
|
*ArgPtr = pp;
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,12 @@ extern BOOL bEcho; /* The echo flag */
|
||||||
extern TCHAR textline[BATCH_BUFFSIZE]; /* Buffer for reading Batch file lines */
|
extern TCHAR textline[BATCH_BUFFSIZE]; /* Buffer for reading Batch file lines */
|
||||||
|
|
||||||
|
|
||||||
LPTSTR FindArg(TCHAR, BOOL *);
|
BOOL
|
||||||
|
FindArg(
|
||||||
|
IN TCHAR Char,
|
||||||
|
OUT PCTSTR* ArgPtr,
|
||||||
|
OUT BOOL* IsParam0);
|
||||||
|
|
||||||
VOID ExitBatch(VOID);
|
VOID ExitBatch(VOID);
|
||||||
VOID ExitAllBatches(VOID);
|
VOID ExitAllBatches(VOID);
|
||||||
INT Batch(LPTSTR, LPTSTR, LPTSTR, PARSED_COMMAND *);
|
INT Batch(LPTSTR, LPTSTR, LPTSTR, PARSED_COMMAND *);
|
||||||
|
|
|
@ -964,8 +964,10 @@ GetEnvVarOrSpecial(LPCTSTR varName)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Handle the %~var syntax */
|
/* Handle the %~var syntax */
|
||||||
static LPTSTR
|
static PCTSTR
|
||||||
GetEnhancedVar(TCHAR **pFormat, LPTSTR (*GetVar)(TCHAR, BOOL *))
|
GetEnhancedVar(
|
||||||
|
IN OUT PCTSTR* pFormat,
|
||||||
|
IN BOOL (*GetVar)(TCHAR, PCTSTR*, BOOL*))
|
||||||
{
|
{
|
||||||
static const TCHAR ModifierTable[] = _T("dpnxfsatz");
|
static const TCHAR ModifierTable[] = _T("dpnxfsatz");
|
||||||
enum {
|
enum {
|
||||||
|
@ -980,28 +982,68 @@ GetEnhancedVar(TCHAR **pFormat, LPTSTR (*GetVar)(TCHAR, BOOL *))
|
||||||
M_SIZE = 256, /* Z: file size */
|
M_SIZE = 256, /* Z: file size */
|
||||||
} Modifiers = 0;
|
} Modifiers = 0;
|
||||||
|
|
||||||
TCHAR *Format, *FormatEnd;
|
PCTSTR Format, FormatEnd;
|
||||||
TCHAR *PathVarName = NULL;
|
PCTSTR PathVarName = NULL;
|
||||||
LPTSTR Variable;
|
PCTSTR Variable;
|
||||||
TCHAR *VarEnd;
|
PCTSTR VarEnd;
|
||||||
BOOL VariableIsParam0;
|
BOOL VariableIsParam0;
|
||||||
TCHAR FullPath[MAX_PATH];
|
TCHAR FullPath[MAX_PATH];
|
||||||
TCHAR FixedPath[MAX_PATH], *Filename, *Extension;
|
TCHAR FixedPath[MAX_PATH];
|
||||||
|
PTCHAR Filename, Extension;
|
||||||
HANDLE hFind;
|
HANDLE hFind;
|
||||||
WIN32_FIND_DATA w32fd;
|
WIN32_FIND_DATA w32fd;
|
||||||
TCHAR *In, *Out;
|
PTCHAR In, Out;
|
||||||
|
|
||||||
static TCHAR Result[CMDLINE_LENGTH];
|
static TCHAR Result[CMDLINE_LENGTH];
|
||||||
|
|
||||||
/* There is ambiguity between modifier characters and FOR variables;
|
/* Check whether the current character is a recognized variable.
|
||||||
* the rule that cmd uses is to pick the longest possible match.
|
* If it is not, then restore the previous one: there is indeed
|
||||||
* For example, if there is a %n variable, then out of %~anxnd,
|
* ambiguity between modifier characters and FOR variables;
|
||||||
* %~anxn will be substituted rather than just %~an. */
|
* the rule that CMD uses is to pick the longest possible match.
|
||||||
|
* This case can happen if we have a FOR-variable specification
|
||||||
|
* of the following form:
|
||||||
|
*
|
||||||
|
* %~<modifiers><actual FOR variable character><other characters>
|
||||||
|
*
|
||||||
|
* where the FOR variable character is also a similar to a modifier,
|
||||||
|
* but should not be interpreted as is, and the following other
|
||||||
|
* characters are not part of the possible modifier characters, and
|
||||||
|
* are unrelated to the FOR variable (they can be part of a command).
|
||||||
|
* For example, if there is a %n variable, then out of %~anxnd,
|
||||||
|
* %~anxn will be substituted rather than just %~an.
|
||||||
|
*
|
||||||
|
* In the following examples, all characters 'd','p','n','x' are valid modifiers.
|
||||||
|
*
|
||||||
|
* 1. In this example, the FOR variable character is 'x' and the actual
|
||||||
|
* modifiers are 'dpn'. Parsing will first determine that 'dpnx'
|
||||||
|
* are modifiers, with the possible (last) valid variable being 'x',
|
||||||
|
* and will stop at the letter 'g'. Since 'g' is not a valid
|
||||||
|
* variable, then the actual variable is the lattest one 'x',
|
||||||
|
* and the modifiers are then actually 'dpn'.
|
||||||
|
* The FOR-loop will then display the %x variable formatted with 'dpn'
|
||||||
|
* and will append any other characters following, 'g'.
|
||||||
|
*
|
||||||
|
* C:\Temp>for %x in (foo.exe bar.txt) do @echo %~dpnxg
|
||||||
|
* C:\Temp\foog
|
||||||
|
* C:\Temp\barg
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* 2. In this second example, the FOR variable character is 'g' and
|
||||||
|
* the actual modifiers are 'dpnx'. Parsing will determine also that
|
||||||
|
* the possible (last) valid variable could be 'x', but since it's
|
||||||
|
* not present in the FOR-variables list, it won't be the case.
|
||||||
|
* This means that the actual FOR variable character must follow,
|
||||||
|
* in this case, 'g'.
|
||||||
|
*
|
||||||
|
* C:\Temp>for %g in (foo.exe bar.txt) do @echo %~dpnxg
|
||||||
|
* C:\Temp\foo.exe
|
||||||
|
* C:\Temp\bar.txt
|
||||||
|
*/
|
||||||
|
|
||||||
/* First, go through as many modifier characters as possible */
|
/* First, go through as many modifier characters as possible */
|
||||||
FormatEnd = Format = *pFormat;
|
FormatEnd = Format = *pFormat;
|
||||||
while (*FormatEnd && _tcschr(ModifierTable, _totlower(*FormatEnd)))
|
while (*FormatEnd && _tcschr(ModifierTable, _totlower(*FormatEnd)))
|
||||||
FormatEnd++;
|
++FormatEnd;
|
||||||
|
|
||||||
if (*FormatEnd == _T('$'))
|
if (*FormatEnd == _T('$'))
|
||||||
{
|
{
|
||||||
|
@ -1012,48 +1054,52 @@ GetEnhancedVar(TCHAR **pFormat, LPTSTR (*GetVar)(TCHAR, BOOL *))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* Must be immediately followed by the variable */
|
/* Must be immediately followed by the variable */
|
||||||
Variable = GetVar(*++FormatEnd, &VariableIsParam0);
|
if (!GetVar(*++FormatEnd, &Variable, &VariableIsParam0))
|
||||||
if (!Variable)
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Backtrack if necessary to get a variable name match */
|
/* Backtrack if necessary to get a variable name match */
|
||||||
while (!(Variable = GetVar(*FormatEnd, &VariableIsParam0)))
|
while (!GetVar(*FormatEnd, &Variable, &VariableIsParam0))
|
||||||
{
|
{
|
||||||
if (FormatEnd == Format)
|
if (FormatEnd == Format)
|
||||||
return NULL;
|
return NULL;
|
||||||
FormatEnd--;
|
--FormatEnd;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (; Format < FormatEnd && *Format != _T('$'); Format++)
|
|
||||||
Modifiers |= 1 << (_tcschr(ModifierTable, _totlower(*Format)) - ModifierTable);
|
|
||||||
|
|
||||||
*pFormat = FormatEnd + 1;
|
*pFormat = FormatEnd + 1;
|
||||||
|
|
||||||
|
/* If the variable is empty, return an empty string */
|
||||||
|
if (!Variable || !*Variable)
|
||||||
|
return _T("");
|
||||||
|
|
||||||
/* Exclude the leading and trailing quotes */
|
/* Exclude the leading and trailing quotes */
|
||||||
VarEnd = &Variable[_tcslen(Variable)];
|
VarEnd = &Variable[_tcslen(Variable)];
|
||||||
if (*Variable == _T('"'))
|
if (*Variable == _T('"'))
|
||||||
{
|
{
|
||||||
Variable++;
|
++Variable;
|
||||||
if (VarEnd > Variable && VarEnd[-1] == _T('"'))
|
if (VarEnd > Variable && VarEnd[-1] == _T('"'))
|
||||||
VarEnd--;
|
--VarEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((char *)VarEnd - (char *)Variable >= sizeof Result)
|
if ((ULONG_PTR)VarEnd - (ULONG_PTR)Variable >= sizeof(Result))
|
||||||
return _T("");
|
return _T("");
|
||||||
memcpy(Result, Variable, (char *)VarEnd - (char *)Variable);
|
memcpy(Result, Variable, (ULONG_PTR)VarEnd - (ULONG_PTR)Variable);
|
||||||
Result[VarEnd - Variable] = _T('\0');
|
Result[VarEnd - Variable] = _T('\0');
|
||||||
|
|
||||||
|
/* Now determine the actual modifiers */
|
||||||
|
for (; Format < FormatEnd && *Format != _T('$'); ++Format)
|
||||||
|
Modifiers |= 1 << (_tcschr(ModifierTable, _totlower(*Format)) - ModifierTable);
|
||||||
|
|
||||||
if (PathVarName)
|
if (PathVarName)
|
||||||
{
|
{
|
||||||
/* $PATH: syntax - search the directories listed in the
|
/* $PATH: syntax - search the directories listed in the
|
||||||
* specified environment variable for the file */
|
* specified environment variable for the file */
|
||||||
LPTSTR PathVar;
|
PTSTR PathVar;
|
||||||
FormatEnd[-1] = _T('\0');
|
((PTSTR)FormatEnd)[-1] = _T('\0'); // FIXME: HACK!
|
||||||
PathVar = GetEnvVar(PathVarName);
|
PathVar = GetEnvVar(PathVarName);
|
||||||
FormatEnd[-1] = _T(':');
|
((PTSTR)FormatEnd)[-1] = _T(':');
|
||||||
if (!PathVar ||
|
if (!PathVar ||
|
||||||
!SearchPath(PathVar, Result, NULL, ARRAYSIZE(FullPath), FullPath, NULL))
|
!SearchPath(PathVar, Result, NULL, ARRAYSIZE(FullPath), FullPath, NULL))
|
||||||
{
|
{
|
||||||
|
@ -1070,6 +1116,7 @@ GetEnhancedVar(TCHAR **pFormat, LPTSTR (*GetVar)(TCHAR, BOOL *))
|
||||||
/* Special case: If the variable is %0 and modifier characters are present,
|
/* Special case: If the variable is %0 and modifier characters are present,
|
||||||
* use the batch file's path (which includes the .bat/.cmd extension)
|
* use the batch file's path (which includes the .bat/.cmd extension)
|
||||||
* rather than the actual %0 variable (which might not). */
|
* rather than the actual %0 variable (which might not). */
|
||||||
|
ASSERT(bc);
|
||||||
_tcscpy(FullPath, bc->BatchFilePath);
|
_tcscpy(FullPath, bc->BatchFilePath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -1103,7 +1150,7 @@ GetEnhancedVar(TCHAR **pFormat, LPTSTR (*GetVar)(TCHAR, BOOL *))
|
||||||
/* If it doesn't exist, just leave the name as it was given */
|
/* If it doesn't exist, just leave the name as it was given */
|
||||||
if (hFind != INVALID_HANDLE_VALUE)
|
if (hFind != INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
LPTSTR FixedComponent = w32fd.cFileName;
|
PTSTR FixedComponent = w32fd.cFileName;
|
||||||
if (*w32fd.cAlternateFileName &&
|
if (*w32fd.cAlternateFileName &&
|
||||||
((Modifiers & M_SHORT) || !_tcsicmp(In, w32fd.cAlternateFileName)))
|
((Modifiers & M_SHORT) || !_tcsicmp(In, w32fd.cAlternateFileName)))
|
||||||
{
|
{
|
||||||
|
@ -1194,7 +1241,7 @@ GetEnhancedVar(TCHAR **pFormat, LPTSTR (*GetVar)(TCHAR, BOOL *))
|
||||||
}
|
}
|
||||||
if (Modifiers & (M_PATH | M_FULL))
|
if (Modifiers & (M_PATH | M_FULL))
|
||||||
{
|
{
|
||||||
memcpy(Out, &FixedPath[2], (char *)Filename - (char *)&FixedPath[2]);
|
memcpy(Out, &FixedPath[2], (ULONG_PTR)Filename - (ULONG_PTR)&FixedPath[2]);
|
||||||
Out += Filename - &FixedPath[2];
|
Out += Filename - &FixedPath[2];
|
||||||
}
|
}
|
||||||
if (Modifiers & (M_NAME | M_FULL))
|
if (Modifiers & (M_NAME | M_FULL))
|
||||||
|
@ -1217,18 +1264,20 @@ GetEnhancedVar(TCHAR **pFormat, LPTSTR (*GetVar)(TCHAR, BOOL *))
|
||||||
return Result;
|
return Result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static LPCTSTR
|
static PCTSTR
|
||||||
GetBatchVar(TCHAR *varName, UINT *varNameLen)
|
GetBatchVar(
|
||||||
|
IN PCTSTR varName,
|
||||||
|
OUT PUINT varNameLen)
|
||||||
{
|
{
|
||||||
LPCTSTR ret;
|
PCTSTR ret;
|
||||||
TCHAR *varNameEnd;
|
PCTSTR varNameEnd;
|
||||||
BOOL dummy;
|
|
||||||
|
|
||||||
*varNameLen = 1;
|
*varNameLen = 1;
|
||||||
|
|
||||||
switch ( *varName )
|
switch (*varName)
|
||||||
{
|
{
|
||||||
case _T('~'):
|
case _T('~'):
|
||||||
|
{
|
||||||
varNameEnd = varName + 1;
|
varNameEnd = varName + 1;
|
||||||
ret = GetEnhancedVar(&varNameEnd, FindArg);
|
ret = GetEnhancedVar(&varNameEnd, FindArg);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
|
@ -1238,6 +1287,7 @@ GetBatchVar(TCHAR *varName, UINT *varNameLen)
|
||||||
}
|
}
|
||||||
*varNameLen = varNameEnd - varName;
|
*varNameLen = varNameEnd - varName;
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
case _T('0'):
|
case _T('0'):
|
||||||
case _T('1'):
|
case _T('1'):
|
||||||
|
@ -1249,7 +1299,13 @@ GetBatchVar(TCHAR *varName, UINT *varNameLen)
|
||||||
case _T('7'):
|
case _T('7'):
|
||||||
case _T('8'):
|
case _T('8'):
|
||||||
case _T('9'):
|
case _T('9'):
|
||||||
return FindArg(*varName, &dummy);
|
{
|
||||||
|
BOOL dummy;
|
||||||
|
if (!FindArg(*varName, &ret, &dummy))
|
||||||
|
return NULL;
|
||||||
|
else
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
case _T('*'):
|
case _T('*'):
|
||||||
/* Copy over the raw params (not including the batch file name) */
|
/* Copy over the raw params (not including the batch file name) */
|
||||||
|
@ -1423,37 +1479,53 @@ too_long:
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Search the list of FOR contexts for a variable */
|
/* Search the list of FOR contexts for a variable */
|
||||||
static LPTSTR
|
static BOOL
|
||||||
FindForVar(TCHAR Var, BOOL *IsParam0)
|
FindForVar(
|
||||||
|
IN TCHAR Var,
|
||||||
|
OUT PCTSTR* VarPtr,
|
||||||
|
OUT BOOL* IsParam0)
|
||||||
{
|
{
|
||||||
PFOR_CONTEXT Ctx;
|
PFOR_CONTEXT Ctx;
|
||||||
|
|
||||||
|
*VarPtr = NULL;
|
||||||
*IsParam0 = FALSE;
|
*IsParam0 = FALSE;
|
||||||
|
|
||||||
for (Ctx = fc; Ctx != NULL; Ctx = Ctx->prev)
|
for (Ctx = fc; Ctx != NULL; Ctx = Ctx->prev)
|
||||||
{
|
{
|
||||||
if ((UINT)(Var - Ctx->firstvar) < Ctx->varcount)
|
if ((UINT)(Var - Ctx->firstvar) < Ctx->varcount)
|
||||||
return Ctx->values[Var - Ctx->firstvar];
|
{
|
||||||
|
*VarPtr = Ctx->values[Var - Ctx->firstvar];
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL
|
BOOL
|
||||||
SubstituteForVars(TCHAR *Src, TCHAR *Dest)
|
SubstituteForVars(
|
||||||
|
IN PCTSTR Src,
|
||||||
|
OUT PTSTR Dest)
|
||||||
{
|
{
|
||||||
TCHAR *DestEnd = &Dest[CMDLINE_LENGTH - 1];
|
PTCHAR DestEnd = &Dest[CMDLINE_LENGTH - 1];
|
||||||
while (*Src)
|
while (*Src)
|
||||||
{
|
{
|
||||||
if (Src[0] == _T('%'))
|
if (Src[0] == _T('%'))
|
||||||
{
|
{
|
||||||
BOOL Dummy;
|
BOOL Dummy;
|
||||||
LPTSTR End = &Src[2];
|
PCTSTR End = &Src[2];
|
||||||
LPTSTR Value = NULL;
|
PCTSTR Value = NULL;
|
||||||
|
|
||||||
if (Src[1] == _T('~'))
|
if (Src[1] == _T('~'))
|
||||||
Value = GetEnhancedVar(&End, FindForVar);
|
Value = GetEnhancedVar(&End, FindForVar);
|
||||||
|
|
||||||
if (!Value)
|
if (!Value && Src[1])
|
||||||
Value = FindForVar(Src[1], &Dummy);
|
{
|
||||||
|
if (FindForVar(Src[1], &Value, &Dummy) && !Value)
|
||||||
|
{
|
||||||
|
/* The variable is empty, return an empty string */
|
||||||
|
Value = _T("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (Value)
|
if (Value)
|
||||||
{
|
{
|
||||||
|
|
|
@ -96,7 +96,12 @@ LPCTSTR GetEnvVarOrSpecial ( LPCTSTR varName );
|
||||||
VOID AddBreakHandler (VOID);
|
VOID AddBreakHandler (VOID);
|
||||||
VOID RemoveBreakHandler (VOID);
|
VOID RemoveBreakHandler (VOID);
|
||||||
BOOL SubstituteVars(TCHAR *Src, TCHAR *Dest, TCHAR Delim);
|
BOOL SubstituteVars(TCHAR *Src, TCHAR *Dest, TCHAR Delim);
|
||||||
BOOL SubstituteForVars(TCHAR *Src, TCHAR *Dest);
|
|
||||||
|
BOOL
|
||||||
|
SubstituteForVars(
|
||||||
|
IN PCTSTR Src,
|
||||||
|
OUT PTSTR Dest);
|
||||||
|
|
||||||
LPTSTR DoDelayedExpansion(LPTSTR Line);
|
LPTSTR DoDelayedExpansion(LPTSTR Line);
|
||||||
INT DoCommand(LPTSTR first, LPTSTR rest, struct _PARSED_COMMAND *Cmd);
|
INT DoCommand(LPTSTR first, LPTSTR rest, struct _PARSED_COMMAND *Cmd);
|
||||||
BOOL ReadLine(TCHAR *commandline, BOOL bMore);
|
BOOL ReadLine(TCHAR *commandline, BOOL bMore);
|
||||||
|
|
Loading…
Reference in a new issue