https://git.reactos.org/?p=reactos.git;a=commitdiff;h=e45bacda07bd73be9e630…
commit e45bacda07bd73be9e630fcd795640ad978c2c54
Author: Michael Stamper <michaelstamper1(a)gmail.com>
AuthorDate: Tue Sep 22 13:49:34 2020 +0000
Commit: GitHub <noreply(a)github.com>
CommitDate: Tue Sep 22 15:49:34 2020 +0200
[PORTCLS] Fix audio stutter with official AC97 driver (#3225)
Replace call to AllocatedBufferSize(), with BufferSize().
Indeed (quoting https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/portcls/nf-po… ):
> The BufferSize() method returns the buffer size that was set by the previous call to IDmaChannel::SetBufferSize(). If SetBufferSize() has not been called since the IDmaChannel::AllocateBuffer() call, BufferSize returns the allocated buffer size. The DMA-channel object does not actually use this value internally. This value is maintained by the object to **allow its various clients to communicate the intended size of the buffer**.
And this is exactly what we want to do.
---
drivers/wdm/audio/backpln/portcls/pin_wavecyclic.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/wdm/audio/backpln/portcls/pin_wavecyclic.cpp b/drivers/wdm/audio/backpln/portcls/pin_wavecyclic.cpp
index e57fbefda74..31d787d08b0 100644
--- a/drivers/wdm/audio/backpln/portcls/pin_wavecyclic.cpp
+++ b/drivers/wdm/audio/backpln/portcls/pin_wavecyclic.cpp
@@ -1300,7 +1300,7 @@ CPortPinWaveCyclic::Init(
m_Stream->SetState(KSSTATE_STOP);
m_State = KSSTATE_STOP;
m_CommonBufferOffset = 0;
- m_CommonBufferSize = m_DmaChannel->AllocatedBufferSize();
+ m_CommonBufferSize = m_DmaChannel->BufferSize();
m_CommonBuffer = m_DmaChannel->SystemAddress();
m_Capture = Capture;
// delay of 10 millisec
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=0695ecbfd641306f9912a…
commit 0695ecbfd641306f9912af865010dbb0ccf20fbd
Author: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
AuthorDate: Sun Jul 26 20:15:25 2020 +0200
Commit: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
CommitDate: Mon Sep 21 03:31:01 2020 +0200
[CMD] FOR: Additional Windows' CMD compatibility "fixes" for FOR /F token parsing command.
This compatibility behaviour implements the buggy behaviour of FOR /F
token parsing that can be observed in Windows' CMD, and that is tested
by the cmd_winetests.
It can be disabled at compile time via the MSCMD_FOR_QUIRKS define.
It fixes additional cmd_winetests, in concert with commit cb2a9c31.
Explanation of the implemented buggy behaviour
==============================================
In principle, the "tokens=x,y,m-n[*]" option describes a list of token
numbers (must be between 1 and 31) that will be assigned into variables.
Theoretically this option does not cumulate: only the latest 'tokens='
specification should be taken into account.
However things are not that simple in practice. First, not all of the
"tokens=" option state is reset when more than one specification is
provided. Second, when specifying a token range, e.g. "1-5", Windows'
CMD just ignores without error ranges that are not specified in
increasing order. Thus for example, a range "5-1" is ignored without
error. Then, token numbers strictly greater than 31 are just ignored,
and if they appear in a range, the whole range is ignored.
Another bug is the following one: suppose that the 'tokens'
specification reads:
"tokens=1-5,1-30" , or: "tokens=1-5,3" ,
i.e. more than one range, that overlap partially. Then the actual total
number of variables will not be of the larger range size, but will be
the sum, instead.
Thus, in the first example, a total of 5 + 30 == 35 variables (> 31) is
allocated, while in the second example, a total of 5 + 1 == 6 variables
is allocated, even if they won't all store data !!
In the first example, only the first 30 FOR variables will be used, and
the 5 others will contain an empty string. In the second example, only
the first 5 FOR variables will be used, and the other one will be empty.
We also see that due to that, the "Variables" buffer of fixed size
cannot always be used (since it can contain at most 32 variables).
Last but not least, when more than one "tokens=" specification is
provided, for example:
"tokens=1-31 tokens=1-20"
a total number of 31 FOR variables (because 31 is the max of 31 and 20)
is allocated, **but** only 20 are actually used, and the 11 others
return an empty string.
And in the specification: "tokens=1-31,* tokens=1-20", a total of
31 + 1 + 20 = 52 variables is initialized, but only the first 20 will
be used, and no "remaining-line" token (the '*' one) is used.
---
base/shell/cmd/for.c | 165 +++++++++++++++++++++++++++++++++++++++++----------
1 file changed, 135 insertions(+), 30 deletions(-)
diff --git a/base/shell/cmd/for.c b/base/shell/cmd/for.c
index 300588bed31..151be0ca2c8 100644
--- a/base/shell/cmd/for.c
+++ b/base/shell/cmd/for.c
@@ -32,6 +32,10 @@
#include "precomp.h"
+/* Enable this define for "buggy" Windows' CMD FOR-command compatibility.
+ * Currently, this enables the buggy behaviour of FOR /F token parsing. */
+#define MSCMD_FOR_QUIRKS
+
/* FOR is a special command, so this function is only used for showing help now */
INT cmd_for(LPTSTR param)
@@ -121,23 +125,27 @@ static LPTSTR ReadFileContents(FILE *InputFile, TCHAR *Buffer)
static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
{
LPTSTR Delims = _T(" \t");
- LPTSTR DelimsEndPtr = NULL;
+ PTCHAR DelimsEndPtr = NULL;
TCHAR DelimsEndChr = _T('\0');
TCHAR Eol = _T(';');
INT SkipLines = 0;
- DWORD Tokens = (1 << 1);
- BOOL RemainderVar = FALSE;
+ DWORD TokensMask = (1 << 1);
+#ifdef MSCMD_FOR_QUIRKS
+ DWORD NumTokens = 1;
+ DWORD RemainderVar = 0;
+#else
+ DWORD NumTokens = 0;
+#endif
TCHAR StringQuote = _T('"');
TCHAR CommandQuote = _T('\'');
LPTSTR Variables[32];
- TCHAR *Start, *End;
- INT i;
+ PTCHAR Start, End;
INT Ret = 0;
if (Cmd->For.Params)
{
TCHAR Quote = 0;
- TCHAR *Param = Cmd->For.Params;
+ PTCHAR Param = Cmd->For.Params;
if (*Param == _T('"') || *Param == _T('\''))
Quote = *Param++;
@@ -161,7 +169,7 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
{
if (*Param == _T(' '))
{
- TCHAR *FirstSpace = Param;
+ PTCHAR FirstSpace = Param;
Param += _tcsspn(Param, _T(" "));
/* Exclude trailing spaces if this is not the last parameter */
if (*Param && *Param != Quote)
@@ -197,24 +205,59 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
}
else if (_tcsnicmp(Param, _T("tokens="), 7) == 0)
{
+#ifdef MSCMD_FOR_QUIRKS
+ DWORD NumToksInSpec = 0; // Number of tokens in this specification.
+#endif
Param += 7;
- /* tokens=x,y,m-n: List of token numbers (must be between
- * 1 and 31) that will be assigned into variables. */
- Tokens = 0;
+ /*
+ * tokens=x,y,m-n: List of token numbers (must be between 1 and 31)
+ * that will be assigned into variables. This option does not cumulate:
+ * only the latest 'tokens=' specification is taken into account.
+ *
+ * NOTE: In MSCMD_FOR_QUIRKS mode, for Windows' CMD compatibility,
+ * not all the tokens-state is reset. This leads to subtle bugs.
+ */
+ TokensMask = 0;
+#ifdef MSCMD_FOR_QUIRKS
+ NumToksInSpec = 0;
+ // Windows' CMD compatibility: bug: the asterisk-token's position is not reset!
+ // RemainderVar = 0;
+#else
+ NumTokens = 0;
+#endif
+
while (*Param && *Param != Quote && *Param != _T('*'))
{
INT First = _tcstol(Param, &Param, 0);
INT Last = First;
+#ifdef MSCMD_FOR_QUIRKS
if (First < 1)
+#else
+ if ((First < 1) || (First > 31))
+#endif
goto error;
if (*Param == _T('-'))
{
/* It's a range of tokens */
Last = _tcstol(Param + 1, &Param, 0);
- if (Last < First || Last > 31)
+#ifdef MSCMD_FOR_QUIRKS
+ /* Ignore the range if the endpoints are not in correct order */
+ if (Last < 1)
+#else
+ if ((Last < First) || (Last > 31))
+#endif
goto error;
}
- Tokens |= (2 << Last) - (1 << First);
+#ifdef MSCMD_FOR_QUIRKS
+ /* Ignore the range if the endpoints are not in correct order */
+ if ((First <= Last) && (Last <= 31))
+ {
+#endif
+ TokensMask |= (2 << Last) - (1 << First);
+#ifdef MSCMD_FOR_QUIRKS
+ NumToksInSpec += (Last - First + 1);
+ }
+#endif
if (*Param != _T(','))
break;
@@ -222,12 +265,19 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
}
/* With an asterisk at the end, an additional variable
* will be created to hold the remainder of the line
- * (after the last token specified). */
+ * (after the last specified token). */
if (*Param == _T('*'))
{
- RemainderVar = TRUE;
+#ifdef MSCMD_FOR_QUIRKS
+ RemainderVar = ++NumToksInSpec;
+#else
+ ++NumTokens;
+#endif
Param++;
}
+#ifdef MSCMD_FOR_QUIRKS
+ NumTokens = max(NumTokens, NumToksInSpec);
+#endif
}
else if (_tcsnicmp(Param, _T("useback"), 7) == 0)
{
@@ -248,12 +298,31 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
}
}
+#ifdef MSCMD_FOR_QUIRKS
+ /* Windows' CMD compatibility: use the wrongly evaluated number of tokens */
+ fc->varcount = NumTokens;
+ /* Allocate a large enough variables array if needed */
+ if (NumTokens <= ARRAYSIZE(Variables))
+ {
+ fc->values = Variables;
+ }
+ else
+ {
+ fc->values = cmd_alloc(fc->varcount * sizeof(*fc->values));
+ if (!fc->values)
+ {
+ error_out_of_memory();
+ return 1;
+ }
+ }
+#else
/* Count how many variables will be set: one for each token,
- * plus maybe one for the remainder */
- fc->varcount = RemainderVar;
- for (i = 1; i < 32; i++)
- fc->varcount += (Tokens >> i & 1);
+ * plus maybe one for the remainder. */
+ fc->varcount = NumTokens;
+ for (NumTokens = 1; NumTokens < 32; ++NumTokens)
+ fc->varcount += (TokensMask >> NumTokens) & 1;
fc->values = Variables;
+#endif
if (*List == StringQuote || *List == CommandQuote)
{
@@ -267,7 +336,7 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
End = List;
while (!ExitingOrGoto(Cmd) && GetNextElement(&Start, &End))
{
- FILE *InputFile;
+ FILE* InputFile;
LPTSTR FullInput, In, NextLine;
INT Skip;
single_element:
@@ -280,13 +349,18 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
}
else if (*Start == CommandQuote && End[-1] == CommandQuote)
{
- /* Read input from a command */
+ /*
+ * Read input from a command. We let the CRT do the ANSI/UNICODE conversion.
+ * NOTE: Should we do that, or instead read in binary mode and
+ * do the conversion by ourselves, using *OUR* current codepage??
+ */
End[-1] = _T('\0');
InputFile = _tpopen(Start + 1, _T("r"));
if (!InputFile)
{
error_bad_command(Start + 1);
- return 1;
+ Ret = 1;
+ goto Quit;
}
FullInput = ReadFileContents(InputFile, Buffer);
_pclose(InputFile);
@@ -302,7 +376,8 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
if (!InputFile)
{
error_sfile_not_found(Start);
- return 1;
+ Ret = 1;
+ goto Quit;
}
FullInput = ReadFileContents(InputFile, Buffer);
fclose(InputFile);
@@ -311,7 +386,8 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
if (!FullInput)
{
error_out_of_memory();
- return 1;
+ Ret = 1;
+ goto Quit;
}
/* Patch the delimiters string */
@@ -326,8 +402,13 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
!ExitingOrGoto(Cmd) && (In != NULL);
In = NextLine)
{
- DWORD RemainingTokens = Tokens;
- LPTSTR *CurVar = Variables;
+ DWORD RemainingTokens = TokensMask;
+ LPTSTR* CurVar = fc->values;
+
+ ZeroMemory(fc->values, fc->varcount * sizeof(*fc->values));
+#ifdef MSCMD_FOR_QUIRKS
+ NumTokens = fc->varcount;
+#endif
NextLine = _tcschr(In, _T('\n'));
if (NextLine)
@@ -341,11 +422,19 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
if (*In == Eol)
continue;
- while ((RemainingTokens >>= 1) != 0)
+ /* Loop as long as we have not reached the end of
+ * the line, and that we have tokens available.
+ * A maximum of 31 tokens will be enumerated. */
+ while (*In && ((RemainingTokens >>= 1) != 0))
{
/* Save pointer to this token in a variable if requested */
if (RemainingTokens & 1)
+ {
+#ifdef MSCMD_FOR_QUIRKS
+ --NumTokens;
+#endif
*CurVar++ = In;
+ }
/* Find end of token */
In += _tcscspn(In, Delims);
/* NULL-terminate it and advance to next token */
@@ -355,11 +444,21 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
In += _tcsspn(In, Delims);
}
}
- /* Save pointer to remainder of line */
- *CurVar = In;
- /* Don't run unless the line had enough tokens to fill at least one variable */
- if (*Variables[0])
+ /* Save pointer to remainder of the line if we need to do so */
+ if (*In)
+#ifdef MSCMD_FOR_QUIRKS
+ if (RemainderVar && (fc->varcount - NumTokens + 1 == RemainderVar))
+#endif
+ {
+ /* NOTE: This sets fc->values[0] at least, if no tokens
+ * were initialized so far, since CurVar is initialized
+ * originally to point to fc->values. */
+ *CurVar = In;
+ }
+
+ /* Don't run unless we have at least one variable filled */
+ if (fc->values[0])
Ret = RunInstance(Cmd);
}
@@ -370,6 +469,12 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
cmd_free(FullInput);
}
+Quit:
+#ifdef MSCMD_FOR_QUIRKS
+ if (fc->values && (fc->values != Variables))
+ cmd_free(fc->values);
+#endif
+
return Ret;
}
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=b62948ef774538fb5066e…
commit b62948ef774538fb5066e88c3b92ce2010bd842c
Author: Oleg Dubinskiy <oleg.dubinskij2013(a)yandex.ua>
AuthorDate: Wed Sep 16 17:54:20 2020 +0300
Commit: Mark Jansen <mark.jansen(a)reactos.org>
CommitDate: Sun Sep 20 19:23:06 2020 +0200
[RAPPS] Use `RegDeleteKeyExW` for deleting the apps strings from registry only if it is available in advapi32
Otherwise, use `RegDeleteKeyW` instead, on Windows versions whose don't have the Ex function (XP SP3 x32, Server 2003 SP0 and earlier).
It will fix regression with Rapps startup there, due to that missing function.
CORE-17264
---
base/applications/rapps/installed.cpp | 22 ++++++++++++++++++++--
1 file changed, 20 insertions(+), 2 deletions(-)
diff --git a/base/applications/rapps/installed.cpp b/base/applications/rapps/installed.cpp
index 4179334a60a..5296c3fc6ae 100644
--- a/base/applications/rapps/installed.cpp
+++ b/base/applications/rapps/installed.cpp
@@ -141,16 +141,34 @@ BOOL CInstalledApplicationInfo::UninstallApplication(BOOL bModify)
return StartProcess(bModify ? szModifyPath : szUninstallString, TRUE);
}
+typedef LSTATUS (WINAPI *RegDeleteKeyExWProc)(HKEY, LPCWSTR, REGSAM, DWORD);
+
LSTATUS CInstalledApplicationInfo::RemoveFromRegistry()
{
ATL::CStringW szFullName = L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + szKeyName;
+ HMODULE hMod = GetModuleHandleW(L"advapi32.dll");
+ RegDeleteKeyExWProc pRegDeleteKeyExW;
- // TODO: if there are subkeys inside, simply RegDeleteKeyExW will fail
+ // TODO: if there are subkeys inside, simply RegDeleteKeyExW
+ // (or RegDeleteKeyW on Server 2003 SP0 and earlier) will fail
// we don't have RegDeleteTree for ReactOS now. (It's a WinVista API)
// write a function to delete all subkeys recursively to solve this
// or consider letting ReactOS having this API
- return RegDeleteKeyExW(IsUserKey ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, szFullName, WowKey, 0);
+ /* Load RegDeleteKeyExW from advapi32.dll if available */
+ if (hMod)
+ {
+ pRegDeleteKeyExW = (RegDeleteKeyExWProc)GetProcAddress(hMod, "RegDeleteKeyExW");
+
+ if (pRegDeleteKeyExW)
+ {
+ /* Return it */
+ return pRegDeleteKeyExW(IsUserKey ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, szFullName, WowKey, 0);
+ }
+ }
+
+ /* Otherwise, return non-Ex function */
+ return RegDeleteKeyW(IsUserKey ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, szFullName);
}
BOOL CInstalledApps::Enum(INT EnumType, APPENUMPROC lpEnumProc, PVOID param)
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=5d5a1a455c9e8b158e4c6…
commit 5d5a1a455c9e8b158e4c6f4c51a1ee20e0da95f0
Author: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
AuthorDate: Sat Jul 4 22:22:07 2020 +0200
Commit: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
CommitDate: Sat Sep 19 19:44:56 2020 +0200
[CMD] FOR: Fix a bug when parsing the "delims=" option in FOR loops.
Suppose the following FOR-loop command, to be run from the command-line
(if using a batch file, double each percent '%' sign):
FOR %l IN ("a,b,c,d,e" "f,g,h,i,j") DO (
FOR /F "delims=, tokens=1-3*" %a IN (%l) DO @echo %a-%b-%c-%d
)
The outermost FOR-loop enumerates the two strings "a,b,c,d,e" and
"f,g,h,i,j" (placed in %l), and parse each of these in turn, splitting
them at each specified delimiter (here only one character) ',' and storing
the results in consecutive tokens %a, %b, %c, %d, with the last token %d
containing all the remaining string (non-split).
The expected result is:
a-b-c-d,e
f-g-h-i,j
However, due to the way the delimiters string specified by the "delims="
option is stored (no stack/heap duplication of the FOR-option substring,
but reading from it directly), during the first run of the innermost
FOR-loop, the option string "delims=, tokens=1-3*" was truncated to just
after the ',' due to the erroneous "delims=" parsing, so that when this
FOR-loop ran for a second time (to deal with the second string), the option
string was already erroneously truncated, without the "tokens=..." part,
so that the parsing results were not stored in the tokens and resulting in:
a-b-c-d,e
f-%b-%c-%d
instead. The solution is to save where the "delims=" string needs to be
cut, but wait until running the actual FOR-loop to terminate it (and
saving the original character too), run the FOR-loop body, and then
restore the original character where termination took place. This allows
having the FOR-loop option string valid for the next execution of the
FOR-loop.
---
base/shell/cmd/for.c | 30 +++++++++++++++++++++++++++---
1 file changed, 27 insertions(+), 3 deletions(-)
diff --git a/base/shell/cmd/for.c b/base/shell/cmd/for.c
index 67a9240c787..300588bed31 100644
--- a/base/shell/cmd/for.c
+++ b/base/shell/cmd/for.c
@@ -121,6 +121,8 @@ static LPTSTR ReadFileContents(FILE *InputFile, TCHAR *Buffer)
static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
{
LPTSTR Delims = _T(" \t");
+ LPTSTR DelimsEndPtr = NULL;
+ TCHAR DelimsEndChr = _T('\0');
TCHAR Eol = _T(';');
INT SkipLines = 0;
DWORD Tokens = (1 << 1);
@@ -148,8 +150,13 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
else if (_tcsnicmp(Param, _T("delims="), 7) == 0)
{
Param += 7;
- /* delims=xxx: Specifies the list of characters that separate tokens */
+ /*
+ * delims=xxx: Specifies the list of characters that separate tokens.
+ * This option does not cumulate: only the latest 'delims=' specification
+ * is taken into account.
+ */
Delims = Param;
+ DelimsEndPtr = NULL;
while (*Param && *Param != Quote)
{
if (*Param == _T(' '))
@@ -158,13 +165,19 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
Param += _tcsspn(Param, _T(" "));
/* Exclude trailing spaces if this is not the last parameter */
if (*Param && *Param != Quote)
- *FirstSpace = _T('\0');
+ {
+ /* Save where the delimiters specification string ends */
+ DelimsEndPtr = FirstSpace;
+ }
break;
}
Param++;
}
if (*Param == Quote)
- *Param++ = _T('\0');
+ {
+ /* Save where the delimiters specification string ends */
+ DelimsEndPtr = Param++;
+ }
}
else if (_tcsnicmp(Param, _T("eol="), 4) == 0)
{
@@ -301,6 +314,13 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
return 1;
}
+ /* Patch the delimiters string */
+ if (DelimsEndPtr)
+ {
+ DelimsEndChr = *DelimsEndPtr;
+ *DelimsEndPtr = _T('\0');
+ }
+
/* Loop over the input line by line */
for (In = FullInput, Skip = SkipLines;
!ExitingOrGoto(Cmd) && (In != NULL);
@@ -343,6 +363,10 @@ static INT ForF(PARSED_COMMAND *Cmd, LPTSTR List, TCHAR *Buffer)
Ret = RunInstance(Cmd);
}
+ /* Restore the delimiters string */
+ if (DelimsEndPtr)
+ *DelimsEndPtr = DelimsEndChr;
+
cmd_free(FullInput);
}
https://git.reactos.org/?p=reactos.git;a=commitdiff;h=cb2a9c31a6c8c207f2285…
commit cb2a9c31a6c8c207f22852c68fc1464dd3b52080
Author: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
AuthorDate: Sat Jul 4 17:40:58 2020 +0200
Commit: Hermès Bélusca-Maïto <hermes.belusca-maito(a)reactos.org>
CommitDate: Sat Sep 19 19:44:54 2020 +0200
[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.
---
base/shell/cmd/batch.c | 26 +++++---
base/shell/cmd/batch.h | 7 ++-
base/shell/cmd/cmd.c | 164 +++++++++++++++++++++++++++++++++++--------------
base/shell/cmd/cmd.h | 7 ++-
4 files changed, 146 insertions(+), 58 deletions(-)
diff --git a/base/shell/cmd/batch.c b/base/shell/cmd/batch.c
index a7dfa2fd11a..e9a1e5aa76e 100644
--- a/base/shell/cmd/batch.c
+++ b/base/shell/cmd/batch.c
@@ -74,29 +74,35 @@ TCHAR textline[BATCH_BUFFSIZE];
/*
* 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 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');
- TRACE ("FindArg: (%d)\n", n);
+ TRACE("FindArg: (%d)\n", n);
+
+ *ArgPtr = NULL;
if (n < 0 || n > 9)
- return NULL;
+ return FALSE;
n = bc->shiftlevel[n];
*IsParam0 = (n == 0);
pp = bc->params;
- /* Step up the strings till we reach the end */
- /* or the one we want */
+ /* Step up the strings till we reach
+ * the end or the one we want. */
while (*pp && n--)
- pp += _tcslen (pp) + 1;
+ pp += _tcslen(pp) + 1;
- return pp;
+ *ArgPtr = pp;
+ return TRUE;
}
diff --git a/base/shell/cmd/batch.h b/base/shell/cmd/batch.h
index de425ee2f11..d40b640e421 100644
--- a/base/shell/cmd/batch.h
+++ b/base/shell/cmd/batch.h
@@ -44,7 +44,12 @@ extern BOOL bEcho; /* The echo flag */
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 ExitAllBatches(VOID);
INT Batch(LPTSTR, LPTSTR, LPTSTR, PARSED_COMMAND *);
diff --git a/base/shell/cmd/cmd.c b/base/shell/cmd/cmd.c
index caa60a466fe..c4d41d0f247 100644
--- a/base/shell/cmd/cmd.c
+++ b/base/shell/cmd/cmd.c
@@ -964,8 +964,10 @@ GetEnvVarOrSpecial(LPCTSTR varName)
}
/* Handle the %~var syntax */
-static LPTSTR
-GetEnhancedVar(TCHAR **pFormat, LPTSTR (*GetVar)(TCHAR, BOOL *))
+static PCTSTR
+GetEnhancedVar(
+ IN OUT PCTSTR* pFormat,
+ IN BOOL (*GetVar)(TCHAR, PCTSTR*, BOOL*))
{
static const TCHAR ModifierTable[] = _T("dpnxfsatz");
enum {
@@ -980,28 +982,68 @@ GetEnhancedVar(TCHAR **pFormat, LPTSTR (*GetVar)(TCHAR, BOOL *))
M_SIZE = 256, /* Z: file size */
} Modifiers = 0;
- TCHAR *Format, *FormatEnd;
- TCHAR *PathVarName = NULL;
- LPTSTR Variable;
- TCHAR *VarEnd;
+ PCTSTR Format, FormatEnd;
+ PCTSTR PathVarName = NULL;
+ PCTSTR Variable;
+ PCTSTR VarEnd;
BOOL VariableIsParam0;
TCHAR FullPath[MAX_PATH];
- TCHAR FixedPath[MAX_PATH], *Filename, *Extension;
+ TCHAR FixedPath[MAX_PATH];
+ PTCHAR Filename, Extension;
HANDLE hFind;
WIN32_FIND_DATA w32fd;
- TCHAR *In, *Out;
+ PTCHAR In, Out;
static TCHAR Result[CMDLINE_LENGTH];
- /* There is ambiguity between modifier characters and FOR variables;
- * the rule that cmd uses is to pick the longest possible match.
- * For example, if there is a %n variable, then out of %~anxnd,
- * %~anxn will be substituted rather than just %~an. */
+ /* Check whether the current character is a recognized variable.
+ * If it is not, then restore the previous one: there is indeed
+ * ambiguity between modifier characters and FOR variables;
+ * 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 */
FormatEnd = Format = *pFormat;
while (*FormatEnd && _tcschr(ModifierTable, _totlower(*FormatEnd)))
- FormatEnd++;
+ ++FormatEnd;
if (*FormatEnd == _T('$'))
{
@@ -1012,48 +1054,52 @@ GetEnhancedVar(TCHAR **pFormat, LPTSTR (*GetVar)(TCHAR, BOOL *))
return NULL;
/* Must be immediately followed by the variable */
- Variable = GetVar(*++FormatEnd, &VariableIsParam0);
- if (!Variable)
+ if (!GetVar(*++FormatEnd, &Variable, &VariableIsParam0))
return NULL;
}
else
{
/* Backtrack if necessary to get a variable name match */
- while (!(Variable = GetVar(*FormatEnd, &VariableIsParam0)))
+ while (!GetVar(*FormatEnd, &Variable, &VariableIsParam0))
{
if (FormatEnd == Format)
return NULL;
- FormatEnd--;
+ --FormatEnd;
}
}
- for (; Format < FormatEnd && *Format != _T('$'); Format++)
- Modifiers |= 1 << (_tcschr(ModifierTable, _totlower(*Format)) - ModifierTable);
-
*pFormat = FormatEnd + 1;
+ /* If the variable is empty, return an empty string */
+ if (!Variable || !*Variable)
+ return _T("");
+
/* Exclude the leading and trailing quotes */
VarEnd = &Variable[_tcslen(Variable)];
if (*Variable == _T('"'))
{
- Variable++;
+ ++Variable;
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("");
- memcpy(Result, Variable, (char *)VarEnd - (char *)Variable);
+ memcpy(Result, Variable, (ULONG_PTR)VarEnd - (ULONG_PTR)Variable);
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)
{
/* $PATH: syntax - search the directories listed in the
* specified environment variable for the file */
- LPTSTR PathVar;
- FormatEnd[-1] = _T('\0');
+ PTSTR PathVar;
+ ((PTSTR)FormatEnd)[-1] = _T('\0'); // FIXME: HACK!
PathVar = GetEnvVar(PathVarName);
- FormatEnd[-1] = _T(':');
+ ((PTSTR)FormatEnd)[-1] = _T(':');
if (!PathVar ||
!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,
* use the batch file's path (which includes the .bat/.cmd extension)
* rather than the actual %0 variable (which might not). */
+ ASSERT(bc);
_tcscpy(FullPath, bc->BatchFilePath);
}
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 (hFind != INVALID_HANDLE_VALUE)
{
- LPTSTR FixedComponent = w32fd.cFileName;
+ PTSTR FixedComponent = w32fd.cFileName;
if (*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))
{
- memcpy(Out, &FixedPath[2], (char *)Filename - (char *)&FixedPath[2]);
+ memcpy(Out, &FixedPath[2], (ULONG_PTR)Filename - (ULONG_PTR)&FixedPath[2]);
Out += Filename - &FixedPath[2];
}
if (Modifiers & (M_NAME | M_FULL))
@@ -1217,18 +1264,20 @@ GetEnhancedVar(TCHAR **pFormat, LPTSTR (*GetVar)(TCHAR, BOOL *))
return Result;
}
-static LPCTSTR
-GetBatchVar(TCHAR *varName, UINT *varNameLen)
+static PCTSTR
+GetBatchVar(
+ IN PCTSTR varName,
+ OUT PUINT varNameLen)
{
- LPCTSTR ret;
- TCHAR *varNameEnd;
- BOOL dummy;
+ PCTSTR ret;
+ PCTSTR varNameEnd;
*varNameLen = 1;
- switch ( *varName )
+ switch (*varName)
{
case _T('~'):
+ {
varNameEnd = varName + 1;
ret = GetEnhancedVar(&varNameEnd, FindArg);
if (!ret)
@@ -1238,6 +1287,7 @@ GetBatchVar(TCHAR *varName, UINT *varNameLen)
}
*varNameLen = varNameEnd - varName;
return ret;
+ }
case _T('0'):
case _T('1'):
@@ -1249,7 +1299,13 @@ GetBatchVar(TCHAR *varName, UINT *varNameLen)
case _T('7'):
case _T('8'):
case _T('9'):
- return FindArg(*varName, &dummy);
+ {
+ BOOL dummy;
+ if (!FindArg(*varName, &ret, &dummy))
+ return NULL;
+ else
+ return ret;
+ }
case _T('*'):
/* 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 */
-static LPTSTR
-FindForVar(TCHAR Var, BOOL *IsParam0)
+static BOOL
+FindForVar(
+ IN TCHAR Var,
+ OUT PCTSTR* VarPtr,
+ OUT BOOL* IsParam0)
{
PFOR_CONTEXT Ctx;
+ *VarPtr = NULL;
*IsParam0 = FALSE;
+
for (Ctx = fc; Ctx != NULL; Ctx = Ctx->prev)
{
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
-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)
{
if (Src[0] == _T('%'))
{
BOOL Dummy;
- LPTSTR End = &Src[2];
- LPTSTR Value = NULL;
+ PCTSTR End = &Src[2];
+ PCTSTR Value = NULL;
if (Src[1] == _T('~'))
Value = GetEnhancedVar(&End, FindForVar);
- if (!Value)
- Value = FindForVar(Src[1], &Dummy);
+ if (!Value && Src[1])
+ {
+ if (FindForVar(Src[1], &Value, &Dummy) && !Value)
+ {
+ /* The variable is empty, return an empty string */
+ Value = _T("");
+ }
+ }
if (Value)
{
diff --git a/base/shell/cmd/cmd.h b/base/shell/cmd/cmd.h
index b3c1c0f1d9d..ac2d7fddd8c 100644
--- a/base/shell/cmd/cmd.h
+++ b/base/shell/cmd/cmd.h
@@ -96,7 +96,12 @@ LPCTSTR GetEnvVarOrSpecial ( LPCTSTR varName );
VOID AddBreakHandler (VOID);
VOID RemoveBreakHandler (VOID);
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);
INT DoCommand(LPTSTR first, LPTSTR rest, struct _PARSED_COMMAND *Cmd);
BOOL ReadLine(TCHAR *commandline, BOOL bMore);