diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index 1d9fc15d775..3c06e13fd76 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -2501,7 +2501,8 @@ void WCMD_give_help (const WCHAR *args) void WCMD_goto (CMD_LIST **cmdList) { WCHAR string[MAX_PATH]; - WCHAR current[MAX_PATH]; + WCHAR *labelend = NULL; + const WCHAR labelEndsW[] = {'>','<','|','&',' ',':','\t','\0'}; /* Do not process any more parts of a processed multipart or multilines command */ if (cmdList) *cmdList = NULL; @@ -2521,25 +2522,38 @@ void WCMD_goto (CMD_LIST **cmdList) { return; } - /* Support goto :label as well as goto label */ + /* Support goto :label as well as goto label plus remove trailing chars */ if (*paramStart == ':') paramStart++; + labelend = strpbrkW(paramStart, labelEndsW); + if (labelend) *labelend = 0x00; + WINE_TRACE("goto label: '%s'\n", wine_dbgstr_w(paramStart)); SetFilePointer (context -> h, 0, NULL, FILE_BEGIN); - while (WCMD_fgets (string, sizeof(string)/sizeof(WCHAR), context -> h)) { + while (*paramStart && + WCMD_fgets (string, sizeof(string)/sizeof(WCHAR), context -> h)) { str = string; - while (isspaceW (*str)) str++; - if (*str == ':') { - DWORD index = 0; - str++; - while (((current[index] = str[index])) && (!isspaceW (current[index]))) - index++; - /* ignore space at the end */ - current[index] = 0; - if (lstrcmpiW (current, paramStart) == 0) return; + /* Ignore leading whitespace or no-echo character */ + while (*str=='@' || isspaceW (*str)) str++; + + /* If the first real character is a : then this is a label */ + if (*str == ':') { + str++; + + /* Skip spaces between : and label */ + while (isspaceW (*str)) str++; + WINE_TRACE("str before brk %s\n", wine_dbgstr_w(str)); + + /* Label ends at whitespace or redirection characters */ + labelend = strpbrkW(str, labelEndsW); + if (labelend) *labelend = 0x00; + WINE_TRACE("comparing found label %s\n", wine_dbgstr_w(str)); + + if (lstrcmpiW (str, paramStart) == 0) return; } } WCMD_output_stderr(WCMD_LoadMessage(WCMD_NOTARGET)); + context -> skip_rest = TRUE; } return; } diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd index 54070bcc13a..f3aa1a9165a 100644 --- a/programs/cmd/tests/test_builtins.cmd +++ b/programs/cmd/tests/test_builtins.cmd @@ -2538,17 +2538,72 @@ echo %ErrorLevel% should still be 7 echo ------------ Testing GOTO ------------ if a==a goto dest1 +echo FAILURE at dest 1 :dest1 echo goto with no leading space worked +if a==a goto :dest1b +echo FAILURE at dest 1b +:dest1b +echo goto with colon and no leading space worked if b==b goto dest2 +echo FAILURE at dest 2 :dest2 echo goto with a leading space worked if c==c goto dest3 +echo FAILURE at dest 3 :dest3 echo goto with a leading tab worked if d==d goto dest4 +echo FAILURE at dest 4 :dest4@space@ echo goto with a following space worked +if e==e goto dest5 +echo FAILURE at dest 5 +:dest5&& echo FAILURE +echo goto with following amphersands worked + +del failure.txt >nul 2>&1 +if f==f goto dest6 +echo FAILURE at dest 6 +:dest6>FAILURE.TXT +if exist FAILURE.TXT echo FAILURE at dest 6 as file exists +echo goto with redirections worked +del FAILURE.TXT >nul 2>&1 + +:: some text that is ignored | dir >cmd_output | another test +if exist cmd_output echo FAILURE at dest 6 as file exists +echo Ignoring double colons worked +del cmd_output >nul 2>&1 + +rem goto a label which does not exist issues an error message and +rem acts the same as goto :EOF, and ensure ::label is never matched +del testgoto.bat >nul 2>&1 +echo goto :dest7 ^>nul 2^>^&1 >> testgoto.bat +echo echo FAILURE at dest 7 - Should have not found label and issued an error plus ended the batch>> testgoto.bat +echo ::dest7>> testgoto.bat +echo echo FAILURE at dest 7 - Incorrectly went to label >> testgoto.bat +call testgoto.bat +del testgoto.bat >nul 2>&1 + +del testgoto.bat >nul 2>&1 +echo goto ::dest8 ^>nul 2^>^&1 >> testgoto.bat +echo echo FAILURE at dest 8 - Should have not found label and issued an error plus ended the batch>> testgoto.bat +echo ::dest8>> testgoto.bat +echo echo FAILURE at dest 8 - Incorrectly went to label >> testgoto.bat +call testgoto.bat +del testgoto.bat >nul 2>&1 + +if g==g goto dest9 +echo FAILURE at dest 9 +:dest91 +echo FAILURE at dest 91 +@ : dest9>rubbish +echo label with mixed whitespace and no echo worked + +if h==h goto :dest10:this is ignored +echo FAILURE at dest 10 +:dest10:this is also ignored +echo Correctly ignored trailing information echo ------------ Testing PATH ------------ set WINE_backup_path=%path% diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index ebfe8cd68f2..dd6ccb5976c 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -1294,9 +1294,15 @@ errorlevel zero, good@or_broken@errorlevel nonzero, bad 7 should still be 7 ------------ Testing GOTO ------------ goto with no leading space worked +goto with colon and no leading space worked goto with a leading space worked goto with a leading tab worked goto with a following space worked +goto with following amphersands worked +goto with redirections worked +Ignoring double colons worked +label with mixed whitespace and no echo worked +Correctly ignored trailing information ------------ Testing PATH ------------ PATH=original PATH=try2 diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 95f29950970..876bbef8249 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -1813,7 +1813,7 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE static const WCHAR forCmd[] = {'f','o','r'}; static const WCHAR ifCmd[] = {'i','f'}; static const WCHAR ifElse[] = {'e','l','s','e'}; - BOOL inRem = FALSE; + BOOL inOneLine = FALSE; BOOL inFor = FALSE; BOOL inIn = FALSE; BOOL inIf = FALSE; @@ -1910,9 +1910,10 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE if (curStringLen == 0 && curCopyTo == curString) { static const WCHAR forDO[] = {'d','o'}; - /* If command starts with 'rem ', ignore any &&, ( etc. */ - if (WCMD_keyword_ws_found(remCmd, sizeof(remCmd)/sizeof(remCmd[0]), curPos)) { - inRem = TRUE; + /* If command starts with 'rem ' or identifies a label, ignore any &&, ( etc. */ + if (WCMD_keyword_ws_found(remCmd, sizeof(remCmd)/sizeof(remCmd[0]), curPos) || + *curPos == ':') { + inOneLine = TRUE; } else if (WCMD_keyword_ws_found(forCmd, sizeof(forCmd)/sizeof(forCmd[0]), curPos)) { inFor = TRUE; @@ -1971,11 +1972,12 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE } } - /* Nothing 'ends' a REM statement and &&, quotes etc are ineffective, - so just use the default processing ie skip character specific - matching below */ - if (!inRem) thisChar = *curPos; - else thisChar = 'X'; /* Character with no special processing */ + /* 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 */ lastWasWhiteSpace = FALSE; /* Will be reset below */ lastWasCaret = FALSE; @@ -2225,7 +2227,7 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE WCHAR *extraData; WINE_TRACE("Need to read more data as outstanding brackets or carets\n"); - inRem = FALSE; + inOneLine = FALSE; prevDelim = CMD_NONE; inQuotes = 0; memset(extraSpace, 0x00, (MAXSTRING+1) * sizeof(WCHAR));