[PATCH 5/8] [cmd] Add support for usebackq (for /f)

Ann and Jason Edmeades jason at edmeades.me.uk
Tue Oct 23 16:32:00 CDT 2012


This patch makes the for /f processing recognize the various syntax
changes that usebackq introduces and fixes the code which spawns an
application so it actually works (for one word program names!).

(Note: The change in batch.c is to trace when a batch is spawned as
during debugging it just silently caused an exit was is a pain to
debug!)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://www.winehq.org/pipermail/wine-patches/attachments/20121023/289e5675/attachment.html>
-------------- next part --------------
From 8bd75bd55a6842cdea6416db2b72c0ef50630902 Mon Sep 17 00:00:00 2001
From: Jason Edmeades <jason at edmeades.me.uk>
Date: Mon, 22 Oct 2012 22:22:28 +0100
Subject: [PATCH 5/8] [cmd] Add support for usebackq (for /f)

This patch makes the for /f processing recognize the various syntax
changes that usebackq introduces and fixes the code which spawns an
application so it actually works (for one word program names!).

(Note: The change in batch.c is to trace when a batch is spawned as
during debugging it just silently caused an exit was is a pain to
debug!)
---
 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
-- 
1.7.9.5


More information about the wine-patches mailing list