[PATCH 7/7] [bug3527] Fix redirect ordering on a command line

Jason Edmeades jason.edmeades at googlemail.com
Tue Sep 11 15:43:08 CDT 2007


(Note: I've finally added myself to the copyright statements given all
the work I've done in all the parts in this directory!)

This patch splits the redirects from the command string, enabling support
of commands of the (wonderfully useful...) form:
   dir >a.a /s
   >a.a echo hello
   echo hello >a.a jason

During testing, I also noticed the error messages were going to stdout
so this patch also fixes that, otherwise 2> is quite hard to test!
---
 programs/cmd/batch.c     |    1 +
 programs/cmd/builtins.c  |   12 +-
 programs/cmd/directory.c |    1 +
 programs/cmd/wcmd.h      |    5 +-
 programs/cmd/wcmdmain.c  |  346 +++++++++++++++++++++++++++++-----------------
 5 files changed, 232 insertions(+), 133 deletions(-)

diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c
index 37eafb1..1d12706 100644
--- a/programs/cmd/batch.c
+++ b/programs/cmd/batch.c
@@ -2,6 +2,7 @@
  * CMD - Wine-compatible command line interface - batch interface.
  *
  * Copyright (C) 1999 D A Pickles
+ * Copyright (C) 2007 J Edmeades
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c
index f58ad94..496ec86 100644
--- a/programs/cmd/builtins.c
+++ b/programs/cmd/builtins.c
@@ -2,6 +2,7 @@
  * CMD - Wine-compatible command line interface - built-in functions.
  *
  * Copyright (C) 1999 D A Pickles
+ * Copyright (C) 2007 J Edmeades
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -832,7 +833,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
           if (*itemStart == '`' || *itemStart == '\'') {
 
               WCHAR temp_path[MAX_PATH], temp_cmd[MAXSTRING];
-              static const WCHAR redirOut[] = {'%','s',' ','>',' ','%','s','\0'};
+              static const WCHAR redirOut[] = {'>','%','s','\0'};
               static const WCHAR cmdW[]     = {'C','M','D','\0'};
 
               /* Remove trailing character */
@@ -844,7 +845,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
 
               /* Execute program and redirect output */
               wsprintf (temp_cmd, redirOut, (itemStart+1), temp_file);
-              WCMD_execute (temp_cmd, NULL, NULL, NULL);
+              WCMD_execute (itemStart, temp_cmd, NULL, NULL, NULL);
 
               /* Open the file, read line by line and process */
               input = CreateFile (temp_file, GENERIC_READ, FILE_SHARE_READ,
@@ -977,7 +978,7 @@ void WCMD_part_execute(CMD_LIST **cmdList, WCHAR *firstcmd, WCHAR *variable,
   /* Process the first command, if there is one */
   if (conditionTRUE && firstcmd && *firstcmd) {
     WCHAR *command = WCMD_strdupW(firstcmd);
-    WCMD_execute (firstcmd, variable, value, cmdList);
+    WCMD_execute (firstcmd, (*cmdList)->redirects, variable, value, cmdList);
     free (command);
   }
 
@@ -1005,7 +1006,8 @@ void WCMD_part_execute(CMD_LIST **cmdList, WCHAR *firstcmd, WCHAR *variable,
       /* Execute any appended to the statement with &&'s */
       if ((*cmdList)->isAmphersand) {
         if (processThese) {
-          WCMD_execute ((*cmdList)->command, variable, value, cmdList);
+          WCMD_execute ((*cmdList)->command, (*cmdList)->redirects, variable,
+                        value, cmdList);
         }
         if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
 
@@ -1033,7 +1035,7 @@ void WCMD_part_execute(CMD_LIST **cmdList, WCHAR *firstcmd, WCHAR *variable,
             /* Skip leading whitespace between condition and the command */
             while (*cmd && (*cmd==' ' || *cmd=='\t')) cmd++;
             if (*cmd) {
-              WCMD_execute (cmd, variable, value, cmdList);
+              WCMD_execute (cmd, (*cmdList)->redirects, variable, value, cmdList);
             }
           }
           if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
diff --git a/programs/cmd/directory.c b/programs/cmd/directory.c
index 0de5bb8..483e496 100644
--- a/programs/cmd/directory.c
+++ b/programs/cmd/directory.c
@@ -2,6 +2,7 @@
  * CMD - Wine-compatible command line interface - Directory functions.
  *
  * Copyright (C) 1999 D A Pickles
+ * Copyright (C) 2007 J Edmeades
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h
index 1d82289..d08ace2 100644
--- a/programs/cmd/wcmd.h
+++ b/programs/cmd/wcmd.h
@@ -2,6 +2,7 @@
  * CMD - Wine-compatible command line interface.
  *
  * Copyright (C) 1999 D A Pickles
+ * Copyright (C) 2007 J Edmeades
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -32,6 +33,7 @@
 
 typedef struct _CMD_LIST {
   WCHAR              *command;     /* Command string to execute                */
+  WCHAR              *redirects;   /* Redirects in place                       */
   struct _CMD_LIST   *nextcommand; /* Next command string to execute           */
   BOOL                isAmphersand;/* Whether follows &&                       */
   int                 bracketDepth;/* How deep bracketing have we got to       */
@@ -103,7 +105,8 @@ BOOL WCMD_ReadFile(const HANDLE hIn, WCHAR *intoBuf, const DWORD maxChars,
 WCHAR    *WCMD_ReadAndParseLine(WCHAR *initialcmd, CMD_LIST **output, HANDLE readFrom);
 CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket, WCHAR *var, WCHAR *val);
 void      WCMD_free_commands(CMD_LIST *cmds);
-void      WCMD_execute (WCHAR *orig_command, WCHAR *parameter, WCHAR *substitution, CMD_LIST **cmdList);
+void      WCMD_execute (WCHAR *orig_command, WCHAR *redirects, WCHAR *parameter,
+                        WCHAR *substitution, CMD_LIST **cmdList);
 
 /*	Data structure to hold context when executing batch files */
 
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c
index 818710c..1d6c29e 100644
--- a/programs/cmd/wcmdmain.c
+++ b/programs/cmd/wcmdmain.c
@@ -2,6 +2,7 @@
  * CMD - Wine-compatible command line interface.
  *
  * Copyright (C) 1999 - 2001 D A Pickles
+ * Copyright (C) 2007 J Edmeades
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -96,6 +97,7 @@ static char  *output_bufA = NULL;
 BOOL unicodePipes = FALSE;
 
 static WCHAR *WCMD_expand_envvar(WCHAR *start, WCHAR *forvar, WCHAR *forVal);
+static void WCMD_output_asis_len(const WCHAR *message, int len, HANDLE device);
 
 /*****************************************************************************
  * Main entry point. This is a console application so we have a main() not a
@@ -544,7 +546,7 @@ void handleExpansion(WCHAR *cmd, BOOL justFors, WCHAR *forVariable, WCHAR *forVa
  */
 
 
-void WCMD_execute (WCHAR *command,
+void WCMD_execute (WCHAR *command, WCHAR *redirects,
                    WCHAR *forVariable, WCHAR *forValue,
                    CMD_LIST **cmdList)
 {
@@ -555,7 +557,6 @@ void WCMD_execute (WCHAR *command,
     WCHAR *whichcmd;
     SECURITY_ATTRIBUTES sa;
     WCHAR *new_cmd;
-    WCHAR *first_redir = NULL;
     HANDLE old_stdhandles[3] = {INVALID_HANDLE_VALUE,
                                 INVALID_HANDLE_VALUE,
                                 INVALID_HANDLE_VALUE};
@@ -614,8 +615,7 @@ void WCMD_execute (WCHAR *command,
  *	Redirect stdin, stdout and/or stderr if required.
  */
 
-    if ((p = strchrW(cmd,'<')) != NULL) {
-      if (first_redir == NULL) first_redir = p;
+    if ((p = strchrW(redirects,'<')) != NULL) {
       h = CreateFile (WCMD_parameter (++p, 0, NULL), GENERIC_READ, FILE_SHARE_READ, &sa, OPEN_EXISTING,
 		FILE_ATTRIBUTE_NORMAL, NULL);
       if (h == INVALID_HANDLE_VALUE) {
@@ -628,15 +628,13 @@ void WCMD_execute (WCHAR *command,
     }
 
     /* Scan the whole command looking for > and 2> */
-    redir = cmd;
+    redir = redirects;
     while (redir != NULL && ((p = strchrW(redir,'>')) != NULL)) {
       int handle = 0;
 
       if (*(p-1)!='2') {
-        if (first_redir == NULL) first_redir = p;
         handle = 1;
       } else {
-        if (first_redir == NULL) first_redir = (p-1);
         handle = 2;
       }
 
@@ -683,9 +681,6 @@ void WCMD_execute (WCHAR *command,
       SetStdHandle (idx_stdhandles[handle], h);
     }
 
-    /* Terminate the command string at <, or first 2> or > */
-    if (first_redir != NULL) *first_redir = '\0';
-
 /*
  * Strip leading whitespaces, and a '@' if supplied
  */
@@ -1267,9 +1262,12 @@ void WCMD_print_error (void) {
 			error_code, GetLastError());
     return;
   }
-  WCMD_output_asis (lpMsgBuf);
+
+  WCMD_output_asis_len(lpMsgBuf, lstrlen(lpMsgBuf),
+                       GetStdHandle(STD_ERROR_HANDLE));
   LocalFree ((HLOCAL)lpMsgBuf);
-  WCMD_output_asis (newline);
+  WCMD_output_asis_len (newline, lstrlen(newline),
+                        GetStdHandle(STD_ERROR_HANDLE));
   return;
 }
 
@@ -1338,7 +1336,7 @@ int p = 0;
  *  and hence required WriteConsoleW to output it, however if file i/o is
  *  redirected, it needs to be WriteFile'd using OEM (not ANSI) format
  */
-static void WCMD_output_asis_len(const WCHAR *message, int len) {
+static void WCMD_output_asis_len(const WCHAR *message, int len, HANDLE device) {
 
     DWORD   nOut= 0;
     DWORD   res = 0;
@@ -1347,8 +1345,7 @@ static void WCMD_output_asis_len(const WCHAR *message, int len) {
     if (!len) return;
 
     /* Try to write as unicode assuming it is to a console */
-    res = WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE),
-                        message, len, &nOut, NULL);
+    res = WriteConsoleW(device, message, len, &nOut, NULL);
 
     /* If writing to console fails, assume its file
        i/o so convert to OEM codepage and output                  */
@@ -1371,10 +1368,10 @@ static void WCMD_output_asis_len(const WCHAR *message, int len) {
         convertedChars = WideCharToMultiByte(GetConsoleOutputCP(), 0, message,
                             len, output_bufA, MAX_WRITECONSOLE_SIZE,
                             "?", &usedDefaultChar);
-        WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), output_bufA, convertedChars,
+        WriteFile(device, output_bufA, convertedChars,
                   &nOut, FALSE);
       } else {
-        WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), message, len*sizeof(WCHAR),
+        WriteFile(device, message, len*sizeof(WCHAR),
                   &nOut, FALSE);
       }
     }
@@ -1400,7 +1397,7 @@ void WCMD_output (const WCHAR *format, ...) {
        string[ret] = '\0';
   }
   va_end(ap);
-  WCMD_output_asis_len(string, ret);
+  WCMD_output_asis_len(string, ret, GetStdHandle(STD_OUTPUT_HANDLE));
 }
 
 
@@ -1451,19 +1448,22 @@ void WCMD_output_asis (const WCHAR *message) {
         ptr++;
       };
       if (*ptr == '\n') ptr++;
-      WCMD_output_asis_len(message, (ptr) ? ptr - message : strlenW(message));
+      WCMD_output_asis_len(message, (ptr) ? ptr - message : strlenW(message),
+                           GetStdHandle(STD_OUTPUT_HANDLE));
       if (ptr) {
         numChars = 0;
         if (++line_count >= max_height - 1) {
           line_count = 0;
-          WCMD_output_asis_len(pagedMessage, strlenW(pagedMessage));
+          WCMD_output_asis_len(pagedMessage, strlenW(pagedMessage),
+                               GetStdHandle(STD_OUTPUT_HANDLE));
           WCMD_ReadFile (GetStdHandle(STD_INPUT_HANDLE), string,
                          sizeof(string)/sizeof(WCHAR), &count, NULL);
         }
       }
     } while (((message = ptr) != NULL) && (*ptr));
   } else {
-    WCMD_output_asis_len(message, lstrlen(message));
+    WCMD_output_asis_len(message, lstrlen(message),
+                         GetStdHandle(STD_OUTPUT_HANDLE));
   }
 }
 
@@ -1544,19 +1544,19 @@ void WCMD_pipe (CMD_LIST **cmdEntry, WCHAR *var, WCHAR *val) {
   p = strchrW(command, '|');
   *p++ = '\0';
   wsprintf (temp_cmd, redirOut, command, temp_file);
-  WCMD_execute (temp_cmd, var, val, cmdEntry);
+  WCMD_execute (temp_cmd, (*cmdEntry)->redirects, var, val, cmdEntry);
   command = p;
   while ((p = strchrW(command, '|'))) {
     *p++ = '\0';
     GetTempFileName (temp_path, cmdW, 0, temp_file2);
     wsprintf (temp_cmd, redirBoth, command, temp_file, temp_file2);
-    WCMD_execute (temp_cmd, var, val, cmdEntry);
+    WCMD_execute (temp_cmd, (*cmdEntry)->redirects, var, val, cmdEntry);
     DeleteFile (temp_file);
     strcpyW (temp_file, temp_file2);
     command = p;
   }
   wsprintf (temp_cmd, redirIn, command, temp_file);
-  WCMD_execute (temp_cmd, var, val, cmdEntry);
+  WCMD_execute (temp_cmd, (*cmdEntry)->redirects, var, val, cmdEntry);
   DeleteFile (temp_file);
 }
 
@@ -1936,7 +1936,8 @@ void WCMD_DumpCommands(CMD_LIST *commands) {
     WCHAR buffer[MAXSTRING];
     CMD_LIST *thisCmd = commands;
     const WCHAR fmt[] = {'%','p',' ','%','c',' ','%','2','.','2','d',' ',
-                         '%','p',' ','%','s','\0'};
+                         '%','p',' ','%','s',' ','R','e','d','i','r',':',
+                         '%','s','\0'};
 
     WINE_TRACE("Parsed line:\n");
     while (thisCmd != NULL) {
@@ -1945,13 +1946,65 @@ void WCMD_DumpCommands(CMD_LIST *commands) {
                thisCmd->isAmphersand?'Y':'N',
                thisCmd->bracketDepth,
                thisCmd->nextcommand,
-               thisCmd->command);
+               thisCmd->command,
+               thisCmd->redirects);
       WINE_TRACE("%s\n", wine_dbgstr_w(buffer));
       thisCmd = thisCmd->nextcommand;
     }
 }
 
 /***************************************************************************
+ * WCMD_addCommand
+ *
+ *   Adds a command to the current command list
+ */
+void WCMD_addCommand(WCHAR *command, int *commandLen,
+                     WCHAR *redirs,  int *redirLen,
+                     WCHAR **copyTo, int **copyToLen,
+                     BOOL   isAmphersand, int curDepth,
+                     CMD_LIST **lastEntry, CMD_LIST **output) {
+
+    CMD_LIST *thisEntry = NULL;
+
+    /* Allocate storage for command */
+    thisEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(CMD_LIST));
+
+    /* Copy in the command */
+    if (command) {
+        thisEntry->command = HeapAlloc(GetProcessHeap(), 0,
+                                      (*commandLen+1) * sizeof(WCHAR));
+        memcpy(thisEntry->command, command, *commandLen * sizeof(WCHAR));
+        thisEntry->command[*commandLen] = 0x00;
+
+        /* Copy in the redirects */
+        thisEntry->redirects = HeapAlloc(GetProcessHeap(), 0,
+                                         (*redirLen+1) * sizeof(WCHAR));
+        memcpy(thisEntry->redirects, redirs, *redirLen * sizeof(WCHAR));
+        thisEntry->redirects[*redirLen] = 0x00;
+
+        /* Reset the lengths */
+        *commandLen   = 0;
+        *redirLen     = 0;
+        *copyToLen    = commandLen;
+        *copyTo       = command;
+
+    } else {
+        thisEntry->command = NULL;
+    }
+
+    /* Fill in other fields */
+    thisEntry->nextcommand = NULL;
+    thisEntry->isAmphersand = isAmphersand;
+    thisEntry->bracketDepth = curDepth;
+    if (*lastEntry) {
+        (*lastEntry)->nextcommand = thisEntry;
+    } else {
+        *output = thisEntry;
+    }
+    *lastEntry = thisEntry;
+}
+
+/***************************************************************************
  * WCMD_ReadAndParseLine
  *
  *   Either uses supplied input or
@@ -1970,9 +2023,12 @@ WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readF
     WCHAR    *curPos;
     BOOL      inQuotes = FALSE;
     WCHAR     curString[MAXSTRING];
-    int       curLen   = 0;
+    int       curStringLen = 0;
+    WCHAR     curRedirs[MAXSTRING];
+    int       curRedirsLen = 0;
+    WCHAR    *curCopyTo;
+    int      *curLen;
     int       curDepth = 0;
-    CMD_LIST *thisEntry = NULL;
     CMD_LIST *lastEntry = NULL;
     BOOL      isAmphersand = FALSE;
     static WCHAR    *extraSpace = NULL;  /* Deliberately never freed */
@@ -1990,6 +2046,7 @@ WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readF
     BOOL      lastWasDo   = FALSE;
     BOOL      lastWasIn   = FALSE;
     BOOL      lastWasElse = FALSE;
+    BOOL      lastWasRedirect = TRUE;
 
     /* Allocate working space for a command read from keyboard, file etc */
     if (!extraSpace)
@@ -2015,8 +2072,12 @@ WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readF
     /* Replace env vars if in a batch context */
     if (context) handleExpansion(extraSpace, FALSE, NULL, NULL);
 
-    /* Start with an empty string */
-    curLen = 0;
+    /* Start with an empty string, copying to the command string */
+    curStringLen = 0;
+    curRedirsLen = 0;
+    curCopyTo    = curString;
+    curLen       = &curStringLen;
+    lastWasRedirect = FALSE;  /* Required for eg spaces between > and filename */
 
     /* Parse every character on the line being processed */
     while (*curPos != 0x00) {
@@ -2024,12 +2085,12 @@ WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readF
       WCHAR thisChar;
 
       /* Debugging AID:
-      WINE_TRACE("Looking at '%c' (len:%d, lws:%d, ows:%d)\n", *curPos, curLen,
+      WINE_TRACE("Looking at '%c' (len:%d, lws:%d, ows:%d)\n", *curPos, *curLen,
                  lastWasWhiteSpace, onlyWhiteSpace);
       */
 
-     /* Certain commands need special handling */
-      if (curLen == 0) {
+      /* Certain commands need special handling */
+      if (curStringLen == 0 && curCopyTo == curString) {
         const WCHAR forDO[]  = {'d','o',' ','\0'};
 
         /* If command starts with 'rem', ignore any &&, ( etc */
@@ -2057,8 +2118,8 @@ WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readF
           inElse = TRUE;
           lastWasElse = TRUE;
           onlyWhiteSpace = TRUE;
-          memcpy(&curString[curLen], curPos, 5*sizeof(WCHAR));
-          curLen+=5;
+          memcpy(&curCopyTo[*curLen], curPos, 5*sizeof(WCHAR));
+          (*curLen)+=5;
           curPos+=5;
           continue;
 
@@ -2071,12 +2132,12 @@ WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readF
           WINE_TRACE("Found DO\n");
           lastWasDo = TRUE;
           onlyWhiteSpace = TRUE;
-          memcpy(&curString[curLen], curPos, 3*sizeof(WCHAR));
-          curLen+=3;
+          memcpy(&curCopyTo[*curLen], curPos, 3*sizeof(WCHAR));
+          (*curLen)+=3;
           curPos+=3;
           continue;
         }
-      } else {
+      } else if (curCopyTo == curString) {
 
         /* Special handling for the 'FOR' command */
         if (inFor && lastWasWhiteSpace) {
@@ -2089,8 +2150,8 @@ WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readF
             WINE_TRACE("Found IN\n");
             lastWasIn = TRUE;
             onlyWhiteSpace = TRUE;
-            memcpy(&curString[curLen], curPos, 3*sizeof(WCHAR));
-            curLen+=3;
+            memcpy(&curCopyTo[*curLen], curPos, 3*sizeof(WCHAR));
+            (*curLen)+=3;
             curPos+=3;
             continue;
           }
@@ -2107,17 +2168,84 @@ WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readF
 
       switch (thisChar) {
 
-      case '\t':/* drop through - ignore whitespace at the start of a command */
-      case ' ': if (curLen > 0)
-                  curString[curLen++] = *curPos;
+      case '=': /* drop through - ignore token delimiters at the start of a command */
+      case ',': /* drop through - ignore token delimiters at the start of a command */
+      case '\t':/* drop through - ignore token delimiters at the start of a command */
+      case ' ':
+                /* If a redirect in place, it ends here */
+                if (!inQuotes && !lastWasRedirect) {
+
+                  /* If finishing off a redirect, add a whitespace delimiter */
+                  if (curCopyTo == curRedirs) {
+                      curCopyTo[(*curLen)++] = ' ';
+                  }
+                  curCopyTo = curString;
+                  curLen = &curStringLen;
+                }
+                if (*curLen > 0) {
+                  curCopyTo[(*curLen)++] = *curPos;
+                }
 
                 /* Remember just processed whitespace */
                 lastWasWhiteSpace = TRUE;
 
                 break;
 
+      case '>': /* drop through - handle redirect chars the same */
+      case '<':
+                /* Make a redirect start here */
+                if (!inQuotes) {
+                  curCopyTo = curRedirs;
+                  curLen = &curRedirsLen;
+                  lastWasRedirect = TRUE;
+                }
+
+                /* See if 1>, 2> etc, in which case we have some patching up
+                   to do                                                     */
+                if (curPos != extraSpace &&
+                    *(curPos-1)>='1' && *(curPos-1)<='9') {
+
+                    curStringLen--;
+                    curString[curStringLen] = 0x00;
+                    curCopyTo[(*curLen)++] = *(curPos-1);
+                }
+
+                curCopyTo[(*curLen)++] = *curPos;
+                break;
+
+      case '|': /* Pipe character only if not || */
+                if (!inQuotes && *(curPos++) == '|') {
+
+                  /* || is an alternative form of && but runs regardless */
+
+                  /* If finishing off a redirect, add a whitespace delimiter */
+                  if (curCopyTo == curRedirs) {
+                      curCopyTo[(*curLen)++] = ' ';
+                  }
+
+                  /* If a redirect in place, it ends here */
+                  curCopyTo = curString;
+                  curLen = &curStringLen;
+                  curCopyTo[(*curLen)++] = *curPos;
+                  lastWasRedirect = FALSE;
+
+                } else if (inQuotes) {
+                  curCopyTo[(*curLen)++] = *curPos;
+                  lastWasRedirect = FALSE;
+
+                } else {
+                  /* Make a redirect start here */
+                  curCopyTo = curRedirs;
+                  curLen = &curRedirsLen;
+                  curCopyTo[(*curLen)++] = *curPos;
+                  lastWasRedirect = TRUE;
+                }
+                break;
+
+
       case '"': inQuotes = !inQuotes;
-                curString[curLen++] = *curPos;
+                curCopyTo[(*curLen)++] = *curPos;
+                lastWasRedirect = FALSE;
                 break;
 
       case '(': /* If a '(' is the first non whitespace in a command portion
@@ -2126,18 +2254,19 @@ WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readF
                 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,
+                           *curLen, inQuotes,
                            onlyWhiteSpace,
                            inFor, lastWasIn, lastWasDo,
                            inIf, inElse, lastWasElse);
+                lastWasRedirect = FALSE;
 
                 /* Ignore open brackets inside the for set */
-                if (curLen == 0 && !inIn) {
+                if (*curLen == 0 && !inIn) {
                   curDepth++;
 
                 /* If in quotes, ignore brackets */
                 } else if (inQuotes) {
-                  curString[curLen++] = *curPos;
+                  curCopyTo[(*curLen)++] = *curPos;
 
                 /* In a FOR loop, an unquoted '(' may occur straight after
                       IN or DO
@@ -2157,98 +2286,71 @@ WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readF
                   }
 
                   /* Add the current command */
-                  thisEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(CMD_LIST));
-                  thisEntry->command = HeapAlloc(GetProcessHeap(), 0,
-                                                 (curLen+1) * sizeof(WCHAR));
-                  memcpy(thisEntry->command, curString, curLen * sizeof(WCHAR));
-                  thisEntry->command[curLen] = 0x00;
-                  curLen = 0;
-                  thisEntry->nextcommand = NULL;
-                  thisEntry->isAmphersand = isAmphersand;
-                  thisEntry->bracketDepth = curDepth;
-                  if (lastEntry) {
-                    lastEntry->nextcommand = thisEntry;
-                  } else {
-                    *output = thisEntry;
-                  }
-                  lastEntry = thisEntry;
+                  WCMD_addCommand(curString, &curStringLen,
+                                  curRedirs, &curRedirsLen,
+                                  &curCopyTo, &curLen,
+                                  isAmphersand, curDepth,
+                                  &lastEntry, output);
 
                   curDepth++;
                 } else {
-                  curString[curLen++] = *curPos;
+                  curCopyTo[(*curLen)++] = *curPos;
                 }
                 break;
 
       case '&': if (!inQuotes && *(curPos+1) == '&') {
                   curPos++; /* Skip other & */
+                  lastWasRedirect = FALSE;
 
                   /* Add an entry to the command list */
-                  if (curLen > 0) {
-                    thisEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(CMD_LIST));
-                    thisEntry->command = HeapAlloc(GetProcessHeap(), 0,
-                                                   (curLen+1) * sizeof(WCHAR));
-                    memcpy(thisEntry->command, curString, curLen * sizeof(WCHAR));
-                    thisEntry->command[curLen] = 0x00;
-                    curLen = 0;
-                    thisEntry->nextcommand = NULL;
-                    thisEntry->isAmphersand = isAmphersand;
-                    thisEntry->bracketDepth = curDepth;
-                    if (lastEntry) {
-                      lastEntry->nextcommand = thisEntry;
-                    } else {
-                      *output = thisEntry;
-                    }
-                    lastEntry = thisEntry;
+                  if (curStringLen > 0) {
+
+                    /* Add the current command */
+                    WCMD_addCommand(curString, &curStringLen,
+                          curRedirs, &curRedirsLen,
+                          &curCopyTo, &curLen,
+                          isAmphersand, curDepth,
+                          &lastEntry, output);
+
                   }
                   isAmphersand = TRUE;
                 } else {
-                  curString[curLen++] = *curPos;
+                  curCopyTo[(*curLen)++] = *curPos;
                 }
                 break;
 
       case ')': if (!inQuotes && curDepth > 0) {
+                  lastWasRedirect = FALSE;
 
                   /* Add the current command if there is one */
-                  if (curLen) {
-                    thisEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(CMD_LIST));
-                    thisEntry->command = HeapAlloc(GetProcessHeap(), 0,
-                                                   (curLen+1) * sizeof(WCHAR));
-                    memcpy(thisEntry->command, curString, curLen * sizeof(WCHAR));
-                    thisEntry->command[curLen] = 0x00;
-                    curLen = 0;
-                    thisEntry->nextcommand = NULL;
-                    thisEntry->isAmphersand = isAmphersand;
-                    thisEntry->bracketDepth = curDepth;
-                    if (lastEntry) {
-                      lastEntry->nextcommand = thisEntry;
-                    } else {
-                      *output = thisEntry;
-                    }
-                    lastEntry = thisEntry;
+                  if (curStringLen) {
+
+                      /* Add the current command */
+                      WCMD_addCommand(curString, &curStringLen,
+                            curRedirs, &curRedirsLen,
+                            &curCopyTo, &curLen,
+                            isAmphersand, curDepth,
+                            &lastEntry, output);
                   }
 
                   /* Add an empty entry to the command list */
-                  thisEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(CMD_LIST));
-                  thisEntry->command = NULL;
-                  thisEntry->nextcommand = NULL;
-                  thisEntry->isAmphersand = FALSE;
-                  thisEntry->bracketDepth = curDepth;
+                  isAmphersand = FALSE;
+                  WCMD_addCommand(NULL, &curStringLen,
+                        curRedirs, &curRedirsLen,
+                        &curCopyTo, &curLen,
+                        isAmphersand, curDepth,
+                        &lastEntry, output);
                   curDepth--;
-                  if (lastEntry) {
-                    lastEntry->nextcommand = thisEntry;
-                  } else {
-                    *output = thisEntry;
-                  }
-                  lastEntry = thisEntry;
 
                   /* Leave inIn if necessary */
                   if (inIn) inIn =  FALSE;
                 } else {
-                  curString[curLen++] = *curPos;
+                  curCopyTo[(*curLen)++] = *curPos;
                 }
                 break;
       default:
-                curString[curLen++] = *curPos;
+                lastWasRedirect = FALSE;
+                curCopyTo[(*curLen)++] = *curPos;
       }
 
       curPos++;
@@ -2264,24 +2366,14 @@ WCHAR *WCMD_ReadAndParseLine(WCHAR *optionalcmd, CMD_LIST **output, HANDLE readF
       }
 
       /* If we have reached the end, add this command into the list */
-      if (*curPos == 0x00 && curLen > 0) {
+      if (*curPos == 0x00 && *curLen > 0) {
 
           /* Add an entry to the command list */
-          thisEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(CMD_LIST));
-          thisEntry->command = HeapAlloc(GetProcessHeap(), 0,
-                                         (curLen+1) * sizeof(WCHAR));
-          memcpy(thisEntry->command, curString, curLen * sizeof(WCHAR));
-          thisEntry->command[curLen] = 0x00;
-          curLen = 0;
-          thisEntry->nextcommand = NULL;
-          thisEntry->isAmphersand = isAmphersand;
-          thisEntry->bracketDepth = curDepth;
-          if (lastEntry) {
-            lastEntry->nextcommand = thisEntry;
-          } else {
-            *output = thisEntry;
-          }
-          lastEntry = thisEntry;
+          WCMD_addCommand(curString, &curStringLen,
+                curRedirs, &curRedirsLen,
+                &curCopyTo, &curLen,
+                isAmphersand, curDepth,
+                &lastEntry, output);
       }
 
       /* If we have reached the end of the string, see if bracketing outstanding */
@@ -2340,10 +2432,10 @@ CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket,
 
         WINE_TRACE("Executing command: '%s'\n", wine_dbgstr_w(thisCmd->command));
 
-        if (strchrW(thisCmd->command,'|') != NULL) {
+        if (strchrW(thisCmd->redirects,'|') != NULL) {
           WCMD_pipe (&thisCmd, var, val);
         } else {
-          WCMD_execute (thisCmd->command, var, val, &thisCmd);
+          WCMD_execute (thisCmd->command, thisCmd->redirects, var, val, &thisCmd);
         }
       }
 
-- 
1.5.0




More information about the wine-patches mailing list