This is part XXIX of the rewrite of cmd engine.
It's time to tackle the lexer... So far, I left it mainly untouched. As it's a &#:!_%% piece of code. It maintains (or actually tries to maintain) a state machine with a dozen of boolean variables. Which makes the code quickly unreadable... Not speaking of changing it...
[ Exercice for the reader: think of the boolean variables as a set of ] [ binary digits, which is the base two representation of the state number. ] [ Rewrite the code using a single state number in order to get rid of ] [ all the boolean variables. ] [ -- Good luck. -- ]
There's a small amount of known bugs in the lexer (some in bugzilla, some I got from direct reports -- thanks Hans --, others from local testings).
This is the first MR (out of 3) to go for that lexer rewrite.
Basically, it's done with: - reusing the already parsed token stack to get back to the state for lexer, - reducing leaves directly (tokens for which we can from first character(s) work on end condition) instead of handling every character in the state machine, - factorizing (eg end of line was handled at two different places, needless to say there "slight" differences in the two parts).
The good news: LoC for lexer (after third MR) is reduced by 30% and fixes most of bugzilla entries related to cmd's lexer. The bad news: wait for bug reports.
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/tests/test_builtins.cmd | 4 +++- programs/cmd/tests/test_builtins.cmd.exp | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd index 1253e5fc2a5..696eda17e2c 100644 --- a/programs/cmd/tests/test_builtins.cmd +++ b/programs/cmd/tests/test_builtins.cmd @@ -1522,11 +1522,13 @@ if 1 == 0 ( @tab@ ) else echo block containing two lines with just tab seems to work :: -echo @if 1 == 1 (> blockclosing.cmd +set WINE_IDONTEXIST= +echo @if [%%WINE_IDONTEXIST%%] == [] (@tab@> blockclosing.cmd echo echo with closing bracket>> blockclosing.cmd echo )>> blockclosing.cmd cmd.exe /Q /C blockclosing.cmd echo %ERRORLEVEL% ok +echo --- :: echo @if 1 == 1 (> blockclosing.cmd echo echo without closing bracket first>> blockclosing.cmd diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 6137c594359..11bcb29b1e7 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -989,8 +989,9 @@ block containing a line with just space and tab seems to work block containing a line with just tab and space seems to work block containing two lines with just space seems to work block containing two lines with just tab seems to work -with closing bracket -0 ok +@todo_wine@with closing bracket +@todo_wine@0 ok +@todo_wine@--- 255 two lines before both blocks 255 nested
From: Eric Pouech epouech@codeweavers.com
https://bugs.winehq.org/show_bug.cgi?id=57877
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/tests/test_builtins.cmd | 1 - programs/cmd/tests/test_builtins.cmd.exp | 5 +-- programs/cmd/wcmdmain.c | 50 +++++++++--------------- 3 files changed, 20 insertions(+), 36 deletions(-)
diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd index 696eda17e2c..f23da28c6d0 100644 --- a/programs/cmd/tests/test_builtins.cmd +++ b/programs/cmd/tests/test_builtins.cmd @@ -1528,7 +1528,6 @@ echo echo with closing bracket>> blockclosing.cmd echo )>> blockclosing.cmd cmd.exe /Q /C blockclosing.cmd echo %ERRORLEVEL% ok -echo --- :: echo @if 1 == 1 (> blockclosing.cmd echo echo without closing bracket first>> blockclosing.cmd diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 11bcb29b1e7..6137c594359 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -989,9 +989,8 @@ block containing a line with just space and tab seems to work block containing a line with just tab and space seems to work block containing two lines with just space seems to work block containing two lines with just tab seems to work -@todo_wine@with closing bracket -@todo_wine@0 ok -@todo_wine@--- +with closing bracket +0 ok 255 two lines before both blocks 255 nested diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index dd277aa68c3..afee9a9a6b5 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -2362,6 +2362,11 @@ static BOOL node_builder_expect_token(struct node_builder *builder, enum builder return TRUE; }
+static enum builder_token node_builder_top(const struct node_builder *builder, unsigned d) +{ + return builder->num > d ? builder->stack[builder->num - (d + 1)].token : TKN_EOF; +} + static void redirection_list_append(CMD_REDIRECTION **redir, CMD_REDIRECTION *last) { if (last) @@ -2828,6 +2833,16 @@ static WCHAR *fetch_next_line(BOOL feed, BOOL first_line, WCHAR* buffer) return buffer; }
+static BOOL lexer_can_accept_do(const struct node_builder *builder) +{ + unsigned d = 0; + + if (node_builder_top(builder, d++) != TKN_CLOSEPAR) return FALSE; + while (node_builder_top(builder, d) == TKN_COMMAND || node_builder_top(builder, d) == TKN_EOL) d++; + if (node_builder_top(builder, d++) != TKN_OPENPAR) return FALSE; + return node_builder_top(builder, d) == TKN_IN; +} + /*************************************************************************** * WCMD_ReadAndParseLine * @@ -2854,17 +2869,9 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** int *curLen; static WCHAR *extraSpace = NULL; /* Deliberately never freed */ BOOL inOneLine = FALSE; - BOOL inFor = FALSE; - BOOL inIf = FALSE; - BOOL inElse= FALSE; BOOL onlyWhiteSpace = FALSE; BOOL lastWasWhiteSpace = FALSE; - BOOL lastWasDo = FALSE; - BOOL lastWasIn = FALSE; - BOOL lastWasElse = FALSE; BOOL lastWasRedirect = TRUE; - BOOL ignoreBracket = FALSE; /* Some expressions after if (set) require */ - /* handling brackets as a normal character */ BOOL acceptCommand = TRUE; struct node_builder builder; BOOL ret; @@ -2918,7 +2925,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** inOneLine = TRUE;
} else if (WCMD_keyword_ws_found(L"for", curPos)) { - inFor = TRUE; node_builder_push_token(&builder, TKN_FOR);
curPos = WCMD_skip_leading_spaces(curPos + 3); /* "for */ @@ -2933,8 +2939,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **
node_builder_push_token(&builder, TKN_IF);
- inIf = TRUE; - curPos = WCMD_skip_leading_spaces(curPos + 2); /* "if" */ if (if_condition_parse(curPos, &command, NULL)) { @@ -2951,14 +2955,10 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** &curCopyTo, &curLen);
} - if (WCMD_keyword_ws_found(L"set", curPos)) - ignoreBracket = TRUE; acceptCommand = TRUE; onlyWhiteSpace = TRUE; continue; } else if (WCMD_keyword_ws_found(L"else", curPos)) { - inElse = TRUE; - lastWasElse = TRUE; acceptCommand = TRUE; onlyWhiteSpace = TRUE; node_builder_push_token(&builder, TKN_ELSE); @@ -2969,12 +2969,9 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** /* In a for loop, the DO command will follow a close bracket followed by whitespace, followed by DO, ie closeBracket inserts a NULL entry, curLen is then 0, and all whitespace is skipped */ - } else if (inFor && lastWasIn && WCMD_keyword_ws_found(L"do", curPos)) { + } else if (lexer_can_accept_do(&builder) && WCMD_keyword_ws_found(L"do", curPos)) {
WINE_TRACE("Found 'DO '\n"); - inFor = FALSE; - lastWasIn = FALSE; - lastWasDo = TRUE; acceptCommand = TRUE; onlyWhiteSpace = TRUE;
@@ -2985,7 +2982,7 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** } else if (curCopyTo == curString) {
/* Special handling for the 'FOR' command */ - if (inFor && lastWasWhiteSpace) { + if (node_builder_top(&builder, 0) == TKN_FOR && lastWasWhiteSpace) { WINE_TRACE("Found 'FOR ', comparing next parm: '%s'\n", wine_dbgstr_w(curPos));
if (WCMD_keyword_ws_found(L"in", curPos)) { @@ -2995,7 +2992,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** curRedirs, &curRedirsLen, &curCopyTo, &curLen); node_builder_push_token(&builder, TKN_IN); - lastWasIn = TRUE; onlyWhiteSpace = TRUE; curPos = WCMD_skip_leading_spaces(curPos + 2 /* in */); continue; @@ -3103,13 +3099,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** case '(': /* If a '(' is the first non whitespace in a command portion ie start of line or just after &&, then we read until an unquoted ) is found */ - WINE_TRACE("Found '(' conditions: curLen(%d), inQ(%d), onlyWS(%d)" - ", for(%d, In:%d, Do:%d)" - ", if(%d, else:%d, lwe:%d)\n", - *curLen, inQuotes, - onlyWhiteSpace, - inFor, lastWasIn, lastWasDo, - inIf, inElse, lastWasElse); lastWasRedirect = FALSE;
if (inQuotes) { @@ -3123,9 +3112,7 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** the ELSE and whitespace */ } else if ((acceptCommand && onlyWhiteSpace) || - (inIf && !ignoreBracket) || - (inElse && lastWasElse && onlyWhiteSpace) || - (inFor && (lastWasIn || lastWasDo) && onlyWhiteSpace)) { + (node_builder_top(&builder, 0) == TKN_IN && onlyWhiteSpace)) {
/* Add the current command */ lexer_push_command(&builder, curString, &curStringLen, @@ -3225,7 +3212,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** if (builder.opened_parenthesis > 0 && optionalcmd == NULL) { TRACE("Need to read more data as outstanding brackets or carets\n"); inOneLine = FALSE; - ignoreBracket = FALSE; inQuotes = 0; acceptCommand = TRUE; onlyWhiteSpace = TRUE;
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/wcmdmain.c | 53 ++++++++++------------------------------- 1 file changed, 13 insertions(+), 40 deletions(-)
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index afee9a9a6b5..468adbaebd8 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -2843,6 +2843,15 @@ static BOOL lexer_can_accept_do(const struct node_builder *builder) return node_builder_top(builder, d) == TKN_IN; }
+static BOOL lexer_white_space_only(const WCHAR *string, int len) +{ + int i; + + for (i = 0; i < len; i++) + if (!iswspace(string[i])) return FALSE; + return TRUE; +} + /*************************************************************************** * WCMD_ReadAndParseLine * @@ -2869,8 +2878,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** int *curLen; static WCHAR *extraSpace = NULL; /* Deliberately never freed */ BOOL inOneLine = FALSE; - BOOL onlyWhiteSpace = FALSE; - BOOL lastWasWhiteSpace = FALSE; BOOL lastWasRedirect = TRUE; BOOL acceptCommand = TRUE; struct node_builder builder; @@ -2897,7 +2904,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** curCopyTo = curString; curLen = &curStringLen; lastWasRedirect = FALSE; /* Required e.g. for spaces between > and filename */ - onlyWhiteSpace = TRUE;
curPos = WCMD_strip_for_command_start(curPos); /* Parse every character on the line being processed */ @@ -2906,8 +2912,7 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** WCHAR thisChar;
/* Debugging AID: - WINE_TRACE("Looking at '%c' (len:%d, lws:%d, ows:%d)\n", *curPos, *curLen, - lastWasWhiteSpace, onlyWhiteSpace); + WINE_TRACE("Looking at '%c' (len:%d)\n", *curPos, *curLen); */
/* Prevent overflow caused by the caret escape char */ @@ -2956,11 +2961,9 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **
} acceptCommand = TRUE; - onlyWhiteSpace = TRUE; continue; } else if (WCMD_keyword_ws_found(L"else", curPos)) { acceptCommand = TRUE; - onlyWhiteSpace = TRUE; node_builder_push_token(&builder, TKN_ELSE);
curPos = WCMD_skip_leading_spaces(curPos + 4 /* else */); @@ -2973,7 +2976,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **
WINE_TRACE("Found 'DO '\n"); acceptCommand = TRUE; - onlyWhiteSpace = TRUE;
node_builder_push_token(&builder, TKN_DO); curPos = WCMD_skip_leading_spaces(curPos + 2 /* do */); @@ -2982,7 +2984,7 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** } else if (curCopyTo == curString) {
/* Special handling for the 'FOR' command */ - if (node_builder_top(&builder, 0) == TKN_FOR && lastWasWhiteSpace) { + if (node_builder_top(&builder, 0) == TKN_FOR) { WINE_TRACE("Found 'FOR ', comparing next parm: '%s'\n", wine_dbgstr_w(curPos));
if (WCMD_keyword_ws_found(L"in", curPos)) { @@ -2992,7 +2994,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** curRedirs, &curRedirsLen, &curCopyTo, &curLen); node_builder_push_token(&builder, TKN_IN); - onlyWhiteSpace = TRUE; curPos = WCMD_skip_leading_spaces(curPos + 2 /* in */); continue; } @@ -3006,8 +3007,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** if (!inOneLine) thisChar = *curPos; else thisChar = 'X'; /* Character with no special processing */
- lastWasWhiteSpace = FALSE; /* Will be reset below */ - switch (thisChar) {
case '=': /* drop through - ignore token delimiters at the start of a command */ @@ -3020,8 +3019,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** /* If finishing off a redirect, add a whitespace delimiter */ if (curCopyTo == curRedirs) { curCopyTo[(*curLen)++] = ' '; - if (curStringLen == 0) - onlyWhiteSpace = TRUE; } curCopyTo = curString; curLen = &curStringLen; @@ -3029,10 +3026,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** if (*curLen > 0) { curCopyTo[(*curLen)++] = *curPos; } - - /* Remember just processed whitespace */ - lastWasWhiteSpace = TRUE; - break;
case '>': /* drop through - handle redirect chars the same */ @@ -3080,8 +3073,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** node_builder_push_token(&builder, TKN_BAR); } acceptCommand = TRUE; - onlyWhiteSpace = TRUE; - thisChar = L' '; } else { curCopyTo[(*curLen)++] = *curPos; } @@ -3111,17 +3102,10 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** In an ELSE statement, only allow it straight away after the ELSE and whitespace */ - } else if ((acceptCommand && onlyWhiteSpace) || - (node_builder_top(&builder, 0) == TKN_IN && onlyWhiteSpace)) { - - /* Add the current command */ - lexer_push_command(&builder, curString, &curStringLen, - curRedirs, &curRedirsLen, - &curCopyTo, &curLen); + } else if ((acceptCommand || node_builder_top(&builder, 0) == TKN_IN) && + lexer_white_space_only(curString, curStringLen)) { node_builder_push_token(&builder, TKN_OPENPAR); acceptCommand = TRUE; - onlyWhiteSpace = TRUE; - thisChar = ' '; } else { curCopyTo[(*curLen)++] = *curPos; } @@ -3164,8 +3148,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** node_builder_push_token(&builder, TKN_AMP); } acceptCommand = TRUE; - onlyWhiteSpace = TRUE; - thisChar = ' '; } else { curCopyTo[(*curLen)++] = *curPos; } @@ -3180,8 +3162,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** &curCopyTo, &curLen); node_builder_push_token(&builder, TKN_CLOSEPAR); acceptCommand = FALSE; - onlyWhiteSpace = TRUE; - thisChar = ' '; } else { curCopyTo[(*curLen)++] = *curPos; } @@ -3193,12 +3173,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **
curPos++;
- /* At various times we need to know if we have only skipped whitespace, - so reset this variable and then it will remain true until a non - whitespace is found */ - if ((thisChar != ' ') && (thisChar != '\t') && (thisChar != '\n')) - onlyWhiteSpace = FALSE; - /* If we have reached the end, add this command into the list Do not add command to list if escape char ^ was last */ if (*curPos == L'\0') { @@ -3214,7 +3188,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** inOneLine = FALSE; inQuotes = 0; acceptCommand = TRUE; - onlyWhiteSpace = TRUE;
/* fetch next non empty line */ do {
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/wcmdmain.c | 50 +++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 27 deletions(-)
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 468adbaebd8..2679c1338b7 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -2907,7 +2907,7 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **
curPos = WCMD_strip_for_command_start(curPos); /* Parse every character on the line being processed */ - while (*curPos != 0x00) { + for (;;) {
WCHAR thisChar;
@@ -2921,10 +2921,30 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** return RPL_SYNTAXERROR; }
+ /* If we have reached the end, add this command into the list + Do not add command to list if escape char ^ was last */ + if (*curPos == L'\0') { + /* Add an entry to the command list */ + lexer_push_command(&builder, curString, &curStringLen, + curRedirs, &curRedirsLen, + &curCopyTo, &curLen); + node_builder_push_token(&builder, TKN_EOL); + + /* If we have reached the end of the string, see if bracketing is outstanding */ + if (builder.opened_parenthesis > 0 && optionalcmd == NULL && + (curPos = fetch_next_line(TRUE, FALSE, extraSpace))) + { + TRACE("Need to read more data as outstanding brackets or carets\n"); + inOneLine = FALSE; + inQuotes = 0; + acceptCommand = TRUE; + } + else break; + } + /* Certain commands need special handling */ if (curStringLen == 0 && curCopyTo == curString) { - if (acceptCommand) - curPos = WCMD_strip_for_command_start(curPos); + if (acceptCommand && !*(curPos = WCMD_strip_for_command_start(curPos))) continue; /* If command starts with 'rem ' or identifies a label, ignore any &&, ( etc. */ if (WCMD_keyword_ws_found(L"rem", curPos) || *curPos == ':') { inOneLine = TRUE; @@ -3172,30 +3192,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** }
curPos++; - - /* If we have reached the end, add this command into the list - Do not add command to list if escape char ^ was last */ - if (*curPos == L'\0') { - /* Add an entry to the command list */ - lexer_push_command(&builder, curString, &curStringLen, - curRedirs, &curRedirsLen, - &curCopyTo, &curLen); - node_builder_push_token(&builder, TKN_EOL); - - /* If we have reached the end of the string, see if bracketing is outstanding */ - if (builder.opened_parenthesis > 0 && optionalcmd == NULL) { - TRACE("Need to read more data as outstanding brackets or carets\n"); - inOneLine = FALSE; - inQuotes = 0; - acceptCommand = TRUE; - - /* fetch next non empty line */ - do { - curPos = fetch_next_line(TRUE, FALSE, extraSpace); - } while (curPos && *curPos == L'\0'); - curPos = curPos ? WCMD_strip_for_command_start(curPos) : extraSpace; - } - } }
ret = node_builder_generate(&builder, output);
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/wcmdmain.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 2679c1338b7..05c7a2737e4 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -2877,7 +2877,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** WCHAR *curCopyTo; int *curLen; static WCHAR *extraSpace = NULL; /* Deliberately never freed */ - BOOL inOneLine = FALSE; BOOL lastWasRedirect = TRUE; BOOL acceptCommand = TRUE; struct node_builder builder; @@ -2935,7 +2934,6 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** (curPos = fetch_next_line(TRUE, FALSE, extraSpace))) { TRACE("Need to read more data as outstanding brackets or carets\n"); - inOneLine = FALSE; inQuotes = 0; acceptCommand = TRUE; } @@ -2945,10 +2943,17 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** /* Certain commands need special handling */ if (curStringLen == 0 && curCopyTo == curString) { if (acceptCommand && !*(curPos = WCMD_strip_for_command_start(curPos))) continue; - /* If command starts with 'rem ' or identifies a label, ignore any &&, ( etc. */ - if (WCMD_keyword_ws_found(L"rem", curPos) || *curPos == ':') { - inOneLine = TRUE; - + /* If command starts with 'rem ' or identifies a label, use whole line */ + if (WCMD_keyword_ws_found(L"rem", curPos) || *curPos == L':') { + size_t line_len = wcslen(curPos); + memcpy(curString, curPos, (line_len + 1) * sizeof(WCHAR)); + curPos += line_len; + curStringLen += line_len; + curRedirsLen = 0; /* even '>foo rem' doesn't touch foo */ + lexer_push_command(&builder, curString, &curStringLen, + curRedirs, &curRedirsLen, + &curCopyTo, &curLen); + continue; } else if (WCMD_keyword_ws_found(L"for", curPos)) { node_builder_push_token(&builder, TKN_FOR);
@@ -3020,12 +3025,7 @@ enum read_parse_line WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE ** } }
- /* Nothing 'ends' a one line statement (e.g. REM or :labels mean - the &&, quotes and redirection etc are ineffective, so just force - the use of the default processing by skipping character specific - matching below) */ - if (!inOneLine) thisChar = *curPos; - else thisChar = 'X'; /* Character with no special processing */ + thisChar = *curPos;
switch (thisChar) {