[12/16] CMD.exe: Unify parsing of batch command and command line

Ann & Jason Edmeades us at edmeades.me.uk
Mon Feb 19 18:43:42 CST 2007


The two routines were almost identical and one calls the other - the
differences can be accounted for when a context (ie in batch pgm) is
present, and revolve mostly around removing env vars which aren't found and
putting out the prompt if echo is on
-------------- next part --------------
>From nobody Mon Sep 17 00:00:00 2001
From: Jason Edmeades <us at edmeades.me.uk>
Date: Mon Feb 19 18:13:39 2007 +0000
Subject: [PATCH] Unify the parsing of parameters

Previously, cmd had 2 places where env vars were expanded, and this
means duplicating all the code for the special env vars, plus anything
should the %var:xxx% syntax is added. This patch unifys it into a
single routine, handling the difference between the routines depending
on whether a context is present (command line vs batch pgm).

---

 programs/cmd/batch.c    |  135 -----------------------------------------------
 programs/cmd/wcmd.h     |    6 ++
 programs/cmd/wcmdmain.c |  134 +++++++++++++++++++++++++++++++++++++++++------
 3 files changed, 126 insertions(+), 149 deletions(-)

189a91f1c867684c7996006f505c62e79ec9265d
diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c
index cdc0e63..b4d3382 100755
--- a/programs/cmd/batch.c
+++ b/programs/cmd/batch.c
@@ -20,17 +20,11 @@
 
 #include "wcmd.h"
 
-void WCMD_batch_command (char *line);
-void WCMD_HandleTildaModifiers(char **start, char *forVariable);
-
 extern int echo_mode;
 extern char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
 extern BATCH_CONTEXT *context;
 extern DWORD errorlevel;
 
-/* msdn specified max for Win XP */
-#define MAXSTRING 8192
-
 /****************************************************************************
  * WCMD_batch
  *
@@ -117,7 +111,7 @@ BATCH_CONTEXT *prev_context;
           WCMD_output_asis( "\n");
       }
       if (string[0] != ':') {                      /* Skip over labels */
-          WCMD_batch_command (string);
+          WCMD_process_command (string);
       }
   }
   CloseHandle (h);
@@ -138,133 +132,6 @@ BATCH_CONTEXT *prev_context;
   }
 }
 
-/****************************************************************************
- * WCMD_batch_command
- *
- * Execute one line from a batch file, expanding parameters.
- */
-
-void WCMD_batch_command (char *line) {
-
-DWORD status;
-char cmd1[MAXSTRING],cmd2[MAXSTRING];
-char *p, *s, *t;
-int i;
-
-  /* Get working version of command line */
-  strcpy(cmd1, line);
-
-  /* Expand environment variables in a batch file %{0-9} first  */
-  /*   Then env vars, and if any left (ie use of undefined vars,*/
-  /*   replace with spaces                                      */
-  /* FIXME: Winnt would replace %1%fred%1 with first parm, then */
-  /*   contents of fred, then the digit 1. Would need to remove */
-  /*   ExpandEnvStrings to achieve this                         */
-
-  /* Replace use of %0...%9 and errorlevel*/
-  p = cmd1;
-  while ((p = strchr(p, '%'))) {
-    i = *(p+1) - '0';
-    if (*(p+1) == '~') {
-      WCMD_HandleTildaModifiers(&p, NULL);
-      p++;
-    } else if ((i >= 0) && (i <= 9)) {
-      s = strdup (p+2);
-      t = WCMD_parameter (context -> command, i + context -> shift_count, NULL);
-      strcpy (p, t);
-      strcat (p, s);
-      free (s);
-    } else if (*(p+1)=='*') {
-      char *startOfParms = NULL;
-      s = strdup (p+2);
-      t = WCMD_parameter (context -> command, 1, &startOfParms);
-      if (startOfParms != NULL) strcpy (p, startOfParms);
-      else *p = 0x00;
-      strcat (p, s);
-      free (s);
-
-    /* Handle DATE, TIME, ERRORLEVEL and CD replacements allowing */ 
-    /* override if existing env var called that name              */
-    } else if ((CompareString (LOCALE_USER_DEFAULT, 
-                              NORM_IGNORECASE | SORT_STRINGSORT,
-      	                      (p+1), 11, "ERRORLEVEL%", -1) == 2) &&
-              (GetEnvironmentVariable("ERRORLEVEL", cmd2, 1) == 0) &&
-              (GetLastError() == ERROR_ENVVAR_NOT_FOUND)) {
-      char output[10];
-      sprintf(output, "%d", errorlevel);
-      s = strdup (p+12);
-      strcpy (p, output);
-      strcat (p, s);
-
-    } else if ((CompareString (LOCALE_USER_DEFAULT, 
-                              NORM_IGNORECASE | SORT_STRINGSORT,
-      	                      (p+1), 5, "DATE%", -1) == 2) &&
-              (GetEnvironmentVariable("DATE", cmd2, 1) == 0) &&
-              (GetLastError() == ERROR_ENVVAR_NOT_FOUND)) {
-
-      GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, NULL, 
-                    NULL, cmd2, MAXSTRING); 
-      s = strdup (p+6);
-      strcpy (p, cmd2);
-      strcat (p, s);
-    
-    } else if ((CompareString (LOCALE_USER_DEFAULT, 
-                              NORM_IGNORECASE | SORT_STRINGSORT,
-      	                      (p+1), 5, "TIME%", -1) == 2) &&
-              (GetEnvironmentVariable("TIME", cmd2, 1) == 0) &&
-              (GetLastError() == ERROR_ENVVAR_NOT_FOUND)) {
-      GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, NULL, 
-                        NULL, cmd2, MAXSTRING);
-      s = strdup (p+6);
-      strcpy (p, cmd2);
-      strcat (p, s);
-    
-    } else if ((CompareString (LOCALE_USER_DEFAULT, 
-                              NORM_IGNORECASE | SORT_STRINGSORT,
-      	                      (p+1), 3, "CD%", -1) == 2) &&
-              (GetEnvironmentVariable("CD", cmd2, 1) == 0) &&
-              (GetLastError() == ERROR_ENVVAR_NOT_FOUND)) {
-      GetCurrentDirectory (MAXSTRING, cmd2);
-      s = strdup (p+4);
-      strcpy (p, cmd2);
-      strcat (p, s);
-
-    } else {
-      p++;
-    }
-  }
-
-  /* Now replace environment variables */
-  status = ExpandEnvironmentStrings(cmd1, cmd2, sizeof(cmd2));
-  if (!status) {
-    WCMD_print_error ();
-    return;
-  }
-
-  /* In a batch program, unknown variables are replace by nothing */
-  /* so remove any remaining %var%                                */
-  p = cmd2;
-  while ((p = strchr(p, '%'))) {
-    s = strchr(p+1, '%');
-    if (!s) {
-      *p=0x00;
-    } else {
-      t = strdup(s+1);
-      strcpy(p, t);
-      free(t);
-    }
-  }
-
-  /* Show prompt before batch line IF echo is on */
-  if (echo_mode && (line[0] != '@')) {
-    WCMD_show_prompt();
-    WCMD_output_asis ( cmd2);
-    WCMD_output_asis ( "\n");
-  }
-
-  WCMD_process_command (cmd2);
-}
-
 /*******************************************************************
  * WCMD_parameter - extract a parameter from a command line.
  *
diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h
index cd2ea75..a5089f2 100755
--- a/programs/cmd/wcmd.h
+++ b/programs/cmd/wcmd.h
@@ -77,6 +77,8 @@ char *WCMD_parameter (char *s, int n, ch
 char *WCMD_strtrim_leading_spaces (char *string);
 void WCMD_strtrim_trailing_spaces (char *string);
 void WCMD_opt_s_strip_quotes(char *cmd);
+void WCMD_HandleTildaModifiers(char **start, char *forVariable);
+
 
 /*	Data structure to hold context when executing batch files */
 
@@ -147,3 +149,7 @@ extern const char nyi[];
 extern const char newline[];
 extern const char version_string[];
 extern const char anykey[];
+
+/* msdn specified max for Win XP */
+#define MAXSTRING 8192
+
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c
index 3c3a154..9ffc65d 100755
--- a/programs/cmd/wcmdmain.c
+++ b/programs/cmd/wcmdmain.c
@@ -256,7 +256,6 @@ int main (int argc, char *argv[])
       HeapFree(GetProcessHeap(), 0, cmd);
   }
 
-#if 0
 /*
  *	If there is an AUTOEXEC.BAT file, try to execute it.
  */
@@ -265,6 +264,7 @@ #if 0
   h = CreateFile (string, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
   if (h != INVALID_HANDLE_VALUE) {
     CloseHandle (h);
+#if 0
     WCMD_batch ((char *)"\\autoexec.bat", (char *)"\\autoexec.bat", 0, NULL, INVALID_HANDLE_VALUE);
 #endif
   }
@@ -301,27 +301,104 @@ #endif
 
 void WCMD_process_command (char *command)
 {
-    char *cmd, *p, *s;
+    char *cmd, *p, *s, *t;
+    char temp[MAXSTRING];
     int status, i, len;
     DWORD count, creationDisposition;
     HANDLE h;
     char *whichcmd;
     SECURITY_ATTRIBUTES sa;
-
-/* 
- *	Replace errorlevel with current value (This shrinks in
- *	  place and hence no need to reallocate the memory yet)
- */
-    p = command;
+    char *new_cmd;
+
+   /* Move copy of the command onto the heap so it can be expanded */
+    new_cmd = HeapAlloc( GetProcessHeap(), 0, MAXSTRING );
+    strcpy(new_cmd, command);
+
+    /* For commands in a context (batch program):                  */
+    /*   Expand environment variables in a batch file %{0-9} first */
+    /*     including support for any ~ modifiers                   */
+    /* Additionally:                                               */
+    /*   Expand the DATE, TIME, CD and ERRORLEVEL special names    */
+    /*     allowing environment variable overrides                 */
+
+    /* FIXME: Winnt would replace %1%fred%1 with first parm, then */
+    /*   contents of fred, then the digit 1. Would need to remove */
+    /*   ExpandEnvStrings to achieve this                         */
+    /* NOTE: To support the %PATH:xxx% syntax, also need to do    */
+    /*   manual expansion of environment variables here           */
+
+    p = new_cmd;
     while ((p = strchr(p, '%'))) {
-      if (CompareString (LOCALE_USER_DEFAULT, 
+      i = *(p+1) - '0';
+
+      /* Replace %~ modifications if in batch program */
+      if (context && *(p+1) == '~') {
+        WCMD_HandleTildaModifiers(&p, NULL);
+        p++;
+      
+      /* Replace use of %0...%9 if in batch program*/
+      } else if (context && (i >= 0) && (i <= 9)) {
+        s = strdup (p+2);
+        t = WCMD_parameter (context -> command, i + context -> shift_count, NULL);
+        strcpy (p, t);
+        strcat (p, s);
+        free (s);
+
+      /* Replace use of %* if in batch program*/
+      } else if (context && *(p+1)=='*') {
+        char *startOfParms = NULL;
+        s = strdup (p+2);
+        t = WCMD_parameter (context -> command, 1, &startOfParms);
+        if (startOfParms != NULL) strcpy (p, startOfParms);
+        else *p = 0x00;
+        strcat (p, s);
+        free (s);
+
+      /* Handle DATE, TIME, ERRORLEVEL and CD replacements allowing */ 
+      /* override if existing env var called that name              */
+      } else if ((CompareString (LOCALE_USER_DEFAULT, 
                                 NORM_IGNORECASE | SORT_STRINGSORT,
-                                (p+1), 11, "ERRORLEVEL%", -1) == 2) {
-        char output[10];
-        sprintf(output, "%d", errorlevel);
+                                (p+1), 11, "ERRORLEVEL%", -1) == 2) &&
+                (GetEnvironmentVariable("ERRORLEVEL", temp, 1) == 0) &&
+                (GetLastError() == ERROR_ENVVAR_NOT_FOUND)) {
+        sprintf(temp, "%d", errorlevel);
         s = strdup (p+12);
-        strcpy (p, output);
+        strcpy (p, temp);
+        strcat (p, s);
+
+      } else if ((CompareString (LOCALE_USER_DEFAULT, 
+                                NORM_IGNORECASE | SORT_STRINGSORT,
+                                (p+1), 5, "DATE%", -1) == 2) &&
+                (GetEnvironmentVariable("DATE", temp, 1) == 0) &&
+                (GetLastError() == ERROR_ENVVAR_NOT_FOUND)) {
+
+        GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, NULL, 
+                      NULL, temp, MAXSTRING); 
+        s = strdup (p+6);
+        strcpy (p, temp);
+        strcat (p, s);
+
+      } else if ((CompareString (LOCALE_USER_DEFAULT, 
+                                NORM_IGNORECASE | SORT_STRINGSORT,
+                                (p+1), 5, "TIME%", -1) == 2) &&
+                (GetEnvironmentVariable("TIME", temp, 1) == 0) &&
+                (GetLastError() == ERROR_ENVVAR_NOT_FOUND)) {
+        GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, NULL, 
+                          NULL, temp, MAXSTRING);
+        s = strdup (p+6);
+        strcpy (p, temp);
         strcat (p, s);
+
+      } else if ((CompareString (LOCALE_USER_DEFAULT, 
+                                NORM_IGNORECASE | SORT_STRINGSORT,
+                                (p+1), 3, "CD%", -1) == 2) &&
+                (GetEnvironmentVariable("CD", temp, 1) == 0) &&
+                (GetLastError() == ERROR_ENVVAR_NOT_FOUND)) {
+        GetCurrentDirectory (MAXSTRING, temp);
+        s = strdup (p+4);
+        strcpy (p, temp);
+        strcat (p, s);
+
       } else {
         p++;
       }
@@ -329,14 +406,41 @@ void WCMD_process_command (char *command
 
 /*
  *	Expand up environment variables.
+ *      FIXME: Move this to above, without reallocating
  */
-    len = ExpandEnvironmentStrings (command, NULL, 0);
+    len = ExpandEnvironmentStrings (new_cmd, NULL, 0);
     cmd = HeapAlloc( GetProcessHeap(), 0, len );
-    status = ExpandEnvironmentStrings (command, cmd, len);
+    status = ExpandEnvironmentStrings (new_cmd, cmd, len);
     if (!status) {
       WCMD_print_error ();
       HeapFree( GetProcessHeap(), 0, cmd );
+      HeapFree( GetProcessHeap(), 0, new_cmd );
       return;
+    } else {
+      HeapFree( GetProcessHeap(), 0, new_cmd );
+    }
+
+    /* In a batch program, unknown variables are replace by nothing */
+    /* so remove any remaining %var%                                */
+    if (context) {
+      p = cmd;
+      while ((p = strchr(p, '%'))) {
+        s = strchr(p+1, '%');
+        if (!s) {
+          *p=0x00;
+        } else {
+          t = strdup(s+1);
+          strcpy(p, t);
+          free(t);
+        }
+      }
+
+      /* Show prompt before batch line IF echo is on and in batch program */
+      if (echo_mode && (cmd[0] != '@')) {
+        WCMD_show_prompt();
+        WCMD_output_asis ( cmd);
+        WCMD_output_asis ( "\n");
+      }
     }
 
 /*
-- 
1.3.0



More information about the wine-patches mailing list