Jason Edmeades : cmd: Add support for usebackq (for /f).
Alexandre Julliard
julliard at winehq.org
Wed Oct 24 13:39:40 CDT 2012
Module: wine
Branch: master
Commit: 9171fd145438072d60effda0709b17ddb89437a4
URL: http://source.winehq.org/git/wine.git/?a=commit;h=9171fd145438072d60effda0709b17ddb89437a4
Author: Jason Edmeades <jason at edmeades.me.uk>
Date: Mon Oct 22 22:22:28 2012 +0100
cmd: Add support for usebackq (for /f).
---
programs/cmd/batch.c | 1 +
programs/cmd/builtins.c | 108 ++++++++++++++++++++---------
programs/cmd/tests/test_builtins.cmd | 14 +++-
programs/cmd/tests/test_builtins.cmd.exp | 4 +
4 files changed, 90 insertions(+), 37 deletions(-)
diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c
index 02d1fcf..1c0bc24 100644
--- a/programs/cmd/batch.c
+++ b/programs/cmd/batch.c
@@ -113,6 +113,7 @@ void WCMD_batch (WCHAR *file, WCHAR *command, BOOL called, WCHAR *startLabel, HA
HeapFree(GetProcessHeap(), 0, context->batchfileW);
LocalFree (context);
if ((prev_context != NULL) && (!called)) {
+ WINE_TRACE("Batch completed, but was not 'called' so skipping outer batch too\n");
prev_context -> skip_rest = TRUE;
context = prev_context;
}
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
index 6ecadac..6e252fe 100644
--- a/programs/cmd/builtins.c
+++ b/programs/cmd/builtins.c
@@ -1583,7 +1583,7 @@ static BOOL WCMD_parse_forf_options(WCHAR *options, WCHAR *eol, int *skip,
usebackqW, sizeof(usebackqW)/sizeof(WCHAR)) == CSTR_EQUAL) {
*usebackq = TRUE;
pos = pos + sizeof(usebackqW)/sizeof(WCHAR);
- WINE_FIXME("Found usebackq\n");
+ WINE_TRACE("Found usebackq\n");
/* Save the supplied delims. Slightly odd as space can be a delimiter but only
if you finish the optionsroot string with delims= otherwise the space is
@@ -1727,6 +1727,67 @@ static void WCMD_parse_line(CMD_LIST *cmdStart,
}
/**************************************************************************
+ * WCMD_forf_getinputhandle
+ *
+ * Return a file handle which can be used for reading the input lines,
+ * either to a specific file (which may be quote delimited as we have to
+ * read the parameters in raw mode) or to a command which we need to
+ * execute. The command being executed runs in its own shell and stores
+ * its data in a temporary file.
+ *
+ * Parameters:
+ * usebackq [I] - Indicates whether usebackq is in effect or not
+ * itemStr [I] - The item to be handled, either a filename or
+ * whole command string to execute
+ * iscmd [I] - Identifies whether this is a command or not
+ *
+ * Returns a file handle which can be used to read the input lines from.
+ */
+HANDLE WCMD_forf_getinputhandle(BOOL usebackq, WCHAR *itemstr, BOOL iscmd) {
+ WCHAR temp_str[MAX_PATH];
+ WCHAR temp_file[MAX_PATH];
+ WCHAR temp_cmd[MAXSTRING];
+ HANDLE hinput = INVALID_HANDLE_VALUE;
+ static const WCHAR redirOutW[] = {'>','%','s','\0'};
+ static const WCHAR cmdW[] = {'C','M','D','\0'};
+ static const WCHAR cmdslashcW[] = {'C','M','D','.','E','X','E',' ',
+ '/','C',' ','"','%','s','"','\0'};
+
+ /* Remove leading and trailing character */
+ if ((iscmd && (itemstr[0] == '`' && usebackq)) ||
+ (iscmd && (itemstr[0] == '\'' && !usebackq)) ||
+ (!iscmd && (itemstr[0] == '"' && usebackq)))
+ {
+ itemstr[strlenW(itemstr)-1] = 0x00;
+ itemstr++;
+ }
+
+ if (iscmd) {
+ /* Get temp filename */
+ GetTempPathW(sizeof(temp_str)/sizeof(WCHAR), temp_str);
+ GetTempFileNameW(temp_str, cmdW, 0, temp_file);
+
+ /* Redirect output to the temporary file */
+ wsprintfW(temp_str, redirOutW, temp_file);
+ wsprintfW(temp_cmd, cmdslashcW, itemstr);
+ WINE_TRACE("Issuing '%s' with redirs '%s'\n",
+ wine_dbgstr_w(temp_cmd), wine_dbgstr_w(temp_str));
+ WCMD_execute (temp_cmd, temp_str, NULL, NULL, NULL, FALSE);
+
+ /* Open the file, read line by line and process */
+ hinput = CreateFileW(temp_file, GENERIC_READ, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, NULL);
+
+ } else {
+ /* Open the file, read line by line and process */
+ WINE_TRACE("Reading input to parse from '%s'\n", wine_dbgstr_w(itemstr));
+ hinput = CreateFileW(itemstr, GENERIC_READ, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ }
+ return hinput;
+}
+
+/**************************************************************************
* WCMD_for
*
* Batch file loop processing.
@@ -1764,7 +1825,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
int forf_skip = 0;
WCHAR forf_delims[256];
WCHAR forf_tokens[MAXSTRING];
- BOOL forf_usebackq;
+ BOOL forf_usebackq = FALSE;
/* Handle optional qualifiers (multiple are allowed) */
WCHAR *thisArg = WCMD_parameter(p, parameterNo++, NULL, NULL, FALSE, FALSE);
@@ -1974,41 +2035,25 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
/* else ignore them! */
/* Filesets - either a list of files, or a command to run and parse the output */
- } else if (doFileset && *itemStart != '"') {
+ } else if (doFileset && ((!forf_usebackq && *itemStart != '"') ||
+ (forf_usebackq && *itemStart != '\''))) {
HANDLE input;
- WCHAR temp_file[MAX_PATH];
WINE_TRACE("Processing for filespec from item %d '%s'\n", itemNum,
wine_dbgstr_w(item));
/* If backquote or single quote, we need to launch that command
and parse the results - use a temporary file */
- if (*itemStart == '`' || *itemStart == '\'') {
-
- WCHAR temp_path[MAX_PATH], temp_cmd[MAXSTRING];
- static const WCHAR redirOut[] = {'>','%','s','\0'};
- static const WCHAR cmdW[] = {'C','M','D','\0'};
-
- /* Remove trailing character */
- itemStart[strlenW(itemStart)-1] = 0x00;
+ if ((forf_usebackq && *itemStart == '`') ||
+ (!forf_usebackq && *itemStart == '\'')) {
- /* Get temp filename */
- GetTempPathW(sizeof(temp_path)/sizeof(WCHAR), temp_path);
- GetTempFileNameW(temp_path, cmdW, 0, temp_file);
-
- /* Execute program and redirect output */
- wsprintfW(temp_cmd, redirOut, (itemStart+1), temp_file);
- WCMD_execute (itemStart, temp_cmd, NULL, NULL, NULL, FALSE);
-
- /* Open the file, read line by line and process */
- input = CreateFileW(temp_file, GENERIC_READ, FILE_SHARE_READ,
- NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ /* Use itemstart because the command is the whole set, not just the first token */
+ input = WCMD_forf_getinputhandle(forf_usebackq, itemStart, TRUE);
} else {
- /* Open the file, read line by line and process */
- input = CreateFileW(item, GENERIC_READ, FILE_SHARE_READ,
- NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+ /* Use item because the file to process is just the first item in the set */
+ input = WCMD_forf_getinputhandle(forf_usebackq, item, FALSE);
}
/* Process the input file */
@@ -2020,8 +2065,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
} else {
- WCHAR buffer[MAXSTRING];
-
+ /* Read line by line until end of file */
while (WCMD_fgets(buffer, sizeof(buffer)/sizeof(WCHAR), input)) {
WCMD_parse_line(cmdStart, firstCmd, &cmdEnd, variable, buffer, &doExecuted,
&forf_skip, forf_eol);
@@ -2030,13 +2074,9 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
CloseHandle (input);
}
- /* Delete the temporary file */
- if (*itemStart == '`' || *itemStart == '\'') {
- DeleteFileW(temp_file);
- }
-
/* Filesets - A string literal */
- } else if (doFileset && *itemStart == '"') {
+ } else if (doFileset && ((!forf_usebackq && *itemStart == '"') ||
+ (forf_usebackq && *itemStart == '\''))) {
/* Copy the item away from the global buffer used by WCMD_parameter */
strcpyW(buffer, item);
diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd
index 11b5dbd..acd5a44 100644
--- a/programs/cmd/tests/test_builtins.cmd
+++ b/programs/cmd/tests/test_builtins.cmd
@@ -1147,9 +1147,17 @@ echo.>> bar
echo kkk>>bar
for /f %%k in (foo bar) do echo %%k
for /f %%k in (bar foo) do echo %%k
-rem echo ------ command argument
-rem Not implemented on NT4
-rem FIXME: Not testable right now in wine: not implemented and would need
+echo ------ command argument
+rem Not implemented on NT4, need to skip it as no way to get output otherwise
+if "%CD%"=="" goto :SkipFORFcmdNT4
+for /f %%i in ('echo.Passed1') do echo %%i
+for /f "usebackq" %%i in (`echo.Passed2`) do echo %%i
+for /f usebackq %%i in (`echo.Passed3`) do echo %%i
+goto :ContinueFORF
+:SkipFORFcmdNT4
+for /l %%i in (1,1,3) do echo Missing functionality - Broken%%i
+:ContinueFORF
+rem FIXME: Rest not testable right now in wine: not implemented and would need
rem preliminary grep-like program implementation (e.g. like findstr or fc) even
rem for a simple todo_wine test
rem (for /f "usebackq" %%i in (`echo z a b`) do echo %%i) || echo not supported
diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp
index 8980c1b..66820be 100644
--- a/programs/cmd/tests/test_builtins.cmd.exp
+++ b/programs/cmd/tests/test_builtins.cmd.exp
@@ -809,6 +809,10 @@ kkk
a
b
c
+------ command argument
+Passed1 at or_broken@Missing functionality - Broken1
+Passed2 at or_broken@Missing functionality - Broken2
+Passed3 at or_broken@Missing functionality - Broken3
------ eol option
and at or_broken@Broken NT4 functionality1
Line at or_broken@Broken NT4 functionality2
More information about the wine-cvs
mailing list