Do not use custom alloc/free functions by default.
Do not free resources at process exit.
- You're doing it brick by brick while the OS & CRT do that with a bulldozer better than you do.
This properly strips spaces & dots at the end of the file.
Fixes the infinite loop with CreateProcess calling cmd over and over with e.g. 'cmd /c "some_script.bat "'
Uncovered by recent ShellExecuteEx tests
Dedicated to Katayama for the trigger & Hermès for the tests
We are not ready for enabling ATLASSERT. Enabling ATL assertions takes time to realize. CORE-17505
- Disable ATLASSERT by undefining _DEBUG.
- Revert currently non-fixable codes.
Instead of messing with global variables and the like, we introduce two target properties:
- WITH_CXX_EXCEPTIONS: if you want to use C++ exceptions
- WITH_CXX_RTTI: if you need RTTI in your module
You can use the newly introduced set_target_cpp_properties function, with WITH_EXCEPTIONS and WITH_RTTI arguments
We also introduce two libraries :
- cpprt: for C++ runtime routines
- cppstl: for the C++ standard template library
NB: On GCC, this requires to create imported libraries with the related built-in libraries:libsupc++, limingwex, libstdc++
Finally, we manage the relevant flags with the ad-hoc generator expressions
So, if you don't need exceptions, nor RTTI, nor use any runtime at all: you simply have nothing else to do than add your C++ file to your module
Fix filename completion that could cause a incorrect result when the path
contains "dots". (See also HBelusca@d12169b.)
See CORE-8623 and CORE-1901 (bug introduced in r25896 / 54cf74f).
For example:
- The current directory is `C:\Documents and Settings\Administrator\`, and you
input `".` and press TAB. The completion result would be `".Administrator"`,
which even does not exist.
- You input "some(file).ext", and you remove the final quote (or the quote
and "ext") and you attempt to complete the file name.
- Import two additional fixes from HBelusca@a826730: Fix the search ordering
in the comparisons between szSearch1, szSearch2 and szSearch3.
Co-authored-by: Hermès BÉLUSCA - MAÏTO <hermes.belusca-maito@reactos.org>
- Move initialization of bc->raw_params also in BatchParams().
- The bc->raw_params, i.e. the unparsed batch/CALL parameters obtained
with %*, has any leading and trailing whitespace trimmed
(since Windows 2000+).
- Fail if no parameter is provided.
- The "CALL :label args..." syntax is available only when command extensions
are enabled. Fail if this syntax is used outside of a batch context.
- Reparse the CALL command parameter with the command parser, in order
to accurately parse and interpret it as a possible command (including
escape carets, etc...) and not duplicate the logic.
** CURRENT Windows' CMD-compatibility LIMITATION ** (may be lifted in
a "ROS-specific" running mode of CMD): only allow standard commands to
be specified as parameter of the CALL command.
This reparsing behaviour can be observed in Windows' CMD, by dumping
the interpreted commands after enabling the cmd!fDumpParse flag from
a debugger (using public symbols).
- When reparsing, we should tell the parser to NOT ignore lines that
start with a colon, because in this situation these are to be
considered as valid "commands" (for parsing "CALL :label").
* For Windows' CMD-compatibility, the remaining escape carets need to
be doubled again so that, after the new parser step, they are escaped
back to their original form. But then we also need to do it the "buggy"
way à la Windows, where carets in quotes are doubled either! However
when being re-parsed, since they are in quotes they remain doubled!!
(see "Phase 6" in https://stackoverflow.com/a/4095133/13530036 ).
* A MSCMD_CALL_QUIRKS define allows to disable this buggy behaviour,
and instead tell the parser to not not interpret the escape carets.
- When initializing a new batch context when the "CALL :label" syntax is
used, ensure that we reuse the same batch file position pointer as its
parent, so as to have correct call label ordering behaviour.
That is,
:label
ECHO hi
CALL :label
:label
ECHO bye
should display:
hi
bye
bye
i.e., the CALL calls the second label instead of the first one (and
thus entering into an infinite loop).
Finally, the "CALL :label" syntax strips the first ':' away, so, as a
side-effect, the command "CALL :EOF" fails (otherwise it would perform
a "GOTO :EOF" and succeeds), while "CALL ::EOF" succeeds.
Fixes some cmd_winetests.
All these modifications have been verified with Windows' CMD, either
by using written cmd_rostests and the existing cmd_winetests, or
manually by enabling the flags cmd!fDumpTokens and cmd!fDumpParse
(available in the public symbols) and analyzing how the tokens are
being parsed, as well as the generated command tree.
See also the following links for more details (but remember that these
observations have to be double-checked in Windows' CMD!):
* Parser rules: https://stackoverflow.com/a/4095133/13530036
* Discussion: https://www.dostips.com/forum/viewtopic.php?f=3&t=8355
* Numbers parsing: https://www.dostips.com/forum/viewtopic.php?t=3758
* Label names vs. GOTO and CALL: https://www.dostips.com/forum/viewtopic.php?f=3&t=3803
and: https://www.dostips.com/forum/viewtopic.php?f=3&t=3803&p=55405#p55405
- Fix REM command parsing. A C_COMMAND-like structure should still
be built, so that it can show up during batch command echo. However
some specific handling needs to be done, so use instead a new C_REM
command type.
Escape carets are parsed differently than usual: they are explicitly
kept in the command line and don't participate in line continuations.
Also, the Windows' CMD behaviour is to discards everything before the
last line continuation.
- Prefix operator '@' (the "silent" operator) is parsed as a separate
command. Thus, the command @@foo@bar is parsed as: '@', '@', 'foo@bar'.
- Improve the checks for numbered redirection.
For this purpose, we check whether this is a number, that is in first
position in the current parsing buffer or is preceded by a whitespace-
like separator, including standard command operators (excepting '@' !)
and double-quotes.
- Empty command blocks, i.e. "( )", standing by themselves, or present
in IF or FOR commands, are considered invalid. (The closing parenthesis
is considered "unexpected".)
- Ignore single closing parenthesis when being outside of command blocks,
thus interpreting it as a command, and ignore explicitly everything
following on the same line, including line continuations.
This very specific situation can happen e.g. while running in batch mode,
when jumping to a label present inside a command block.
See the code for a thorough explanation.
- Detect whether a parenthesized block is not terminated at the end
of a command stream (getting a NUL character instead of a newline),
and if so, bail out early instead of entering into an infinite loop.
- Perform a similar check for the parenthesized list in FOR commands.
- Initialize the static 'InsideBlock' value to a known value.
- The '&' operator (multi-commmand) is allowed to have an empty RHS.
When such situation occurs, turn the CurrentTokenType to TOK_END
so as to avoid a parse error later on.
- The main body of a IF statement, or its 'else' clause, as well as
the main body of a FOR statement, must not be empty, otherwise this
is considered a syntax error. If so, call ParseError() that sets
the 'bParseError' flag, and forcing all batch execution to stop.
- Detect whether a division by zero is done, and fail if so.
- Detect whether an invalid number is provided:
* If _tcstol() fails with errno == ERANGE, we've got an overflow or
underflow.
* If the next character where _tcstol() is not a whitespace but is a
character compatible with the first character of an identifier, the
number is invalid.
- Add + to the list of existing unary operators (!,~,-), and parse them
where many of these are present. Indeed, expressions like: +3, -+-+3,
!!-+3 (or with other unary ops, etc.) are valid.
- Operators constituted of more than one characters, can contain
whitespace separating their constituting characters.
Thus, "a + = 3" is equivalent to "a += 3" (and the same for -=, *=,
/=, %=, &=, |= and ^=), and "a < < 3" is equivalent to "a << 3" (and
the same for >>, <<= and >>=).
- After evaluating everything, if unparsed data remains, fail and bail out.
- Return Windows' CMD-compatible errorlevels.
See https://ss64.com/nt/set.html for more details.
Fixes some cmd_winetests.
Observation shows that this is done only when the top-level batch file
is being run (and then terminates); the echo flag is not restored to its
previous state when a child batch file terminates and gives main back to
its parent batch.
- Make sure that non-administrator users can list associations, and
display appropriate error messages when e.g. they don't have sufficient
privileges to perform an operation.
- Make the helper functions all return Win32 values, used as the
ERRORVALUE, except when a specific extension association fails to be
displayed, in which case the ERRORVALUE is normalized to 1.
- Since the 'param' is a modifiable string (that can be modified by the
command, independently of the way it's called), just use it to isolate
the extension by zeroing out the equls-sign separator.
Commands APPEND/DPATH and FTYPE are also concerned by this; however
we do not implement them in our CMD.EXE yet.
These commands set the ERRORLEVEL differently, whether or not they are
run manually from the command-line/from a .BAT file, or from a .CMD file:
- From command-line/.BAT file, these commands set the ERRORLEVEL only if
an error occurs. So, if two commands are run consecutively and the first
one fails, the ERRORLEVEL will remain set even if the second command
succeeds.
- However, when being run from a .CMD file, these command will always
set the ERRORLEVEL. In the example case described above, the second
command that succeeds will reset the ERRORLEVEL to 0.
This behaviour is determined from the top-level batch/script file being
run. This means that, if a .BAT file is first started, then starts a
.CMD file, the commands will still behave the .BAT way; on the opposite,
if a .CMD file is first started, then starts a .BAT file, these commands
will still behave the .CMD way.
To implement this we introduce one global BATCH_TYPE enum variable that
is initialized to the corresponding batch/script file type when the
top-level script is loaded. It is reset to "none" when that script
terminates.
See https://ss64.com/nt/errorlevel.html for more details,
section "Old style .bat Batch files vs .cmd Batch scripts",
and https://groups.google.com/forum/#!msg/microsoft.public.win2000.cmdprompt.admin/XHeUq8oe2wk/LIEViGNmkK0J
(comment by Mark Zbikowski).
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.
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.
CORE-13682
- Split SubstituteVars() into its main loop and a helper SubstituteVar()
that just substitutes only one variable.
- Use this new helper as the basis of the proper implementation of the
delayed expansion of variables.
- Fix a bug introduced in commit 495c82cc, when GetBatchVar() fails.
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.
They are currently specified for documentation purposes (i.e. what
Windows 8+ CMD.EXE can report) but not used yet, since ReactOS does not
support them.