[15/16] CMD.exe: Allow for SET modifiers

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


To support this, expand env vars inline rather than in one call, and support
the %var:xxx=yyy% and %var:~xx,yy% syntax
-------------- next part --------------
>From nobody Mon Sep 17 00:00:00 2001
From: Jason Edmeades <us at edmeades.me.uk>
Date: Mon Feb 19 23:47:23 2007 +0000
Subject: [PATCH] Expand env vars in cmd, and allow = and ~ modifiers

Note: Also fixes %1%FRED%1 case

---

 programs/cmd/wcmdmain.c |  225 +++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 207 insertions(+), 18 deletions(-)

be55f28882a8d6e18405c03065514811b9c14ceb
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c
index 8c4680c..e915251 100755
--- a/programs/cmd/wcmdmain.c
+++ b/programs/cmd/wcmdmain.c
@@ -46,6 +46,8 @@ char quals[MAX_PATH], param1[MAX_PATH], 
 BATCH_CONTEXT *context = NULL;
 static HANDLE old_stdin = INVALID_HANDLE_VALUE, old_stdout = INVALID_HANDLE_VALUE;
 
+static char *WCMD_expand_envvar(char *start);
+
 /*****************************************************************************
  * Main entry point. This is a console application so we have a main() not a
  * winmain().
@@ -303,7 +305,7 @@ void WCMD_process_command (char *command
 {
     char *cmd, *p, *s, *t;
     char temp[MAXSTRING];
-    int status, i, len;
+    int status, i;
     DWORD count, creationDisposition;
     HANDLE h;
     char *whichcmd;
@@ -410,25 +412,10 @@ void WCMD_process_command (char *command
         strcat (p, s);
 
       } else {
-        p++;
+        p = WCMD_expand_envvar(p);
       }
     }
-
-/*
- *	Expand up environment variables.
- *      FIXME: Move this to above, without reallocating
- */
-    len = ExpandEnvironmentStrings (new_cmd, NULL, 0);
-    cmd = HeapAlloc( GetProcessHeap(), 0, 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 );
-    }
+    cmd = new_cmd;
 
     /* In a batch program, unknown variables are replace by nothing */
     /* so remove any remaining %var%                                */
@@ -1135,3 +1122,205 @@ char temp_path[MAX_PATH], temp_file[MAX_
   WCMD_process_command (temp_cmd);
   DeleteFile (temp_file);
 }
+
+/*************************************************************************
+ * WCMD_expand_envvar
+ *
+ *	Expands environment variables, allowing for character substitution
+ */
+static char *WCMD_expand_envvar(char *start) {
+    char *endOfVar = NULL, *s;
+    char *colonpos = NULL;
+    char thisVar[MAXSTRING];
+    char thisVarContents[MAXSTRING];
+    char savedchar = 0x00;
+    int len;
+
+    /* Find the end of the environment variable, and extract name */
+    endOfVar = strchr(start+1, '%');
+    if (endOfVar == NULL) {
+      /* FIXME: Some special conditions here depending opn whether
+         in batch, complex or not, and whether env var exists or not! */
+      return start+1;
+    }
+    strncpy(thisVar, start, (endOfVar - start)+1);
+    thisVar[(endOfVar - start)+1] = 0x00;
+    colonpos = strchr(thisVar+1, ':');
+
+    /* If there's complex substitution, just need %var% for now
+       to get the expanded data to play with                    */
+    if (colonpos) {
+        *colonpos = '%';
+        savedchar = *(colonpos+1);
+        *(colonpos+1) = 0x00;
+    }
+
+    /* Expand to contents, if unchanged, return */
+    len = ExpandEnvironmentStrings(thisVar, thisVarContents, 
+                                     sizeof(thisVarContents));
+    if (len == 0)
+      return endOfVar+1;
+
+    /* In a batch program, unknown env vars are replaced with nothing,
+         note syntax %garbage:1,3% results in anything after the ':'
+         except the %    
+       From the command line, you just get back what you entered      */
+    if (lstrcmpi(thisVar, thisVarContents) == 0) {
+
+      /* Restore the complex part after the compare */
+      if (colonpos) {
+        *colonpos = ':';
+        *(colonpos+1) = savedchar;
+      }
+
+      s = strdup (endOfVar + 1);
+
+      /* Command line - just ignore this */
+      if (context == NULL) return endOfVar+1;
+
+      /* Batch - replace unknown env var with nothing */
+      if (colonpos == NULL) {
+        strcpy (start, s);
+
+      } else {
+        len = strlen(thisVar);
+        thisVar[len-1] = 0x00;
+        /* If %:...% supplied, : is retained */
+        if (colonpos == thisVar+1) {
+          strcpy (start, colonpos);
+        } else {
+          strcpy (start, colonpos+1);
+        }
+        strcat (start, s);
+      }
+      free (s);
+      return start;
+      
+    }
+
+    /* See if we need to do complex substitution (any ':'s), if not
+       then our work here is done                                  */
+    if (colonpos == NULL) {
+      s = strdup (endOfVar + 1);
+      strcpy (start, thisVarContents);
+      strcat (start, s);
+      free(s);
+      return start;
+    } 
+
+    /* Restore complex bit */
+    *colonpos = ':';
+    *(colonpos+1) = savedchar;  
+
+    /* 
+        Handle complex substitutions:
+           xxx=yyy    (replace xxx with yyy)
+           *xxx=yyy   (replace up to and including xxx with yyy)
+           ~x         (from x chars in)
+           ~-x        (from x chars from the end)
+           ~x,y       (from x chars in for y characters)
+           ~x,-y      (from x chars in until y characters from the end)
+     */           
+
+    /* ~ is substring manipulation */
+    if (savedchar == '~') {
+
+      int   substrposition, substrlength;
+      char *commapos = strchr(colonpos+2, ',');
+      char *startCopy;
+
+      substrposition = atol(colonpos+2);
+      if (commapos) substrlength = atol(commapos+1);
+        
+      s = strdup (endOfVar + 1);
+
+      /* Check bounds */
+      if (substrposition >= 0) {
+        startCopy = &thisVarContents[min(substrposition, len)];
+      } else {
+        startCopy = &thisVarContents[max(0, len+substrposition-1)];
+      }
+
+      if (commapos == NULL) {
+        strcpy (start, startCopy); /* Copy the lot */
+      } else if (substrlength < 0) {
+
+        int copybytes = (len+substrlength-1)-(startCopy-thisVarContents);
+        if (copybytes > len) copybytes = len;
+        else if (copybytes < 0) copybytes = 0;
+        strncpy (start, startCopy, copybytes); /* Copy the lot */
+        start[copybytes] = 0x00;
+      } else {
+        strncpy (start, startCopy, substrlength); /* Copy the lot */
+        start[substrlength] = 0x00;
+      }
+
+      strcat (start, s);
+      free(s);
+      return start;
+
+    /* search and replace manipulation */
+    } else {
+      char *equalspos = strstr(colonpos, "=");
+      char *replacewith = equalspos+1;
+      char *found       = NULL;
+      char *searchIn; 
+      char *searchFor;
+
+      s = strdup (endOfVar + 1);
+      if (equalspos == NULL) return start+1;
+
+      /* Null terminate both strings */
+      thisVar[strlen(thisVar)-1] = 0x00;
+      *equalspos = 0x00;
+      
+      /* Since we need to be case insensitive, copy the 2 buffers */
+      searchIn  = strdup(thisVarContents);
+      CharUpperBuff(searchIn, strlen(thisVarContents));
+      searchFor = strdup(colonpos+1);
+      CharUpperBuff(searchFor, strlen(colonpos+1));
+
+
+      /* Handle wildcard case */
+      if (*(colonpos+1) == '*') {
+        /* Search for string to replace */
+        found = strstr(searchIn, searchFor+1);
+
+        if (found) {
+          /* Do replacement */
+          strcpy(start, replacewith);
+          strcat(start, thisVarContents + (found-searchIn) + strlen(searchFor+1));
+          strcat(start, s);
+          free(s);
+        } else {
+          /* Copy as it */
+          strcpy(start, thisVarContents);
+          strcat(start, s);
+        }
+
+      } else {
+        /* Loop replacing all instances */
+        char *lastFound = searchIn;
+        char *outputposn = start;
+
+        *start = 0x00;
+        while ((found = strstr(lastFound, searchFor))) {
+            strncpy(outputposn, 
+                    thisVarContents + (lastFound-searchIn), 
+                    (found - lastFound));
+            outputposn  = outputposn + (found - lastFound);
+            *outputposn = 0x00;
+            strcat(outputposn, replacewith);
+            outputposn = outputposn + strlen(replacewith);
+            lastFound = found + strlen(searchFor);
+        }
+        strcat(outputposn, 
+                thisVarContents + (lastFound-searchIn));
+        strcat(outputposn, s);
+      }
+      free(searchIn);
+      free(searchFor);
+      return start;
+    }
+    return start+1;
+}
-- 
1.3.0



More information about the wine-patches mailing list