FW: [05/16] CMD.exe: Add ~ modifiers for %0-9

Ann & Jason Edmeades us at edmeades.me.uk
Tue Feb 20 11:49:43 CST 2007



-----Original Message-----
From: Ann & Jason Edmeades [mailto:us at edmeades.me.uk] 
Sent: 20 February 2007 00:37
To: 'wine-patches at winehq.org'
Subject: [05/16] CMD.exe: Add ~ modifiers for %0-9

Note: Added infrastructure for 'for' support as well, but not implemented
yet
-------------- next part --------------
>From nobody Mon Sep 17 00:00:00 2001
From: Jason Edmeades <us at edmeades.me.uk>
Date: Sat Feb 17 21:36:08 2007 +0000
Subject: [PATCH] Add %~ modifiers from command extensions support

for %0-9 parameters

---

 programs/cmd/batch.c |  355 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 354 insertions(+), 1 deletions(-)

ed4b03b9fa7b4fa2a8ad2cdb0df1637ce7a2e2b8
diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c
index 9dd365e..4ac6f78 100755
--- a/programs/cmd/batch.c
+++ b/programs/cmd/batch.c
@@ -21,6 +21,7 @@
 #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];
@@ -149,7 +150,10 @@ int i;
   p = cmd1;
   while ((p = strchr(p, '%'))) {
     i = *(p+1) - '0';
-    if ((i >= 0) && (i <= 9)) {
+    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);
@@ -296,3 +300,352 @@ char *p;
   } while ((bytes == 1) && (n > 1));
   return p;
 }
+
+/* _splitpath - copied from winefile as no obvious way to use it otherwise */
+void _splitpath(const CHAR* path, CHAR* drv, CHAR* dir, CHAR* name, CHAR* ext)
+{
+        const CHAR* end; /* end of processed string */
+	const CHAR* p;	 /* search pointer */
+	const CHAR* s;	 /* copy pointer */
+
+	/* extract drive name */
+	if (path[0] && path[1]==':') {
+		if (drv) {
+			*drv++ = *path++;
+			*drv++ = *path++;
+			*drv = '\0';
+		}
+	} else if (drv)
+		*drv = '\0';
+
+	/* search for end of string or stream separator */
+	for(end=path; *end && *end!=':'; )
+		end++;
+
+	/* search for begin of file extension */
+	for(p=end; p>path && *--p!='\\' && *p!='/'; )
+		if (*p == '.') {
+			end = p;
+			break;
+		}
+
+	if (ext)
+		for(s=end; (*ext=*s++); )
+			ext++;
+
+	/* search for end of directory name */
+	for(p=end; p>path; )
+		if (*--p=='\\' || *p=='/') {
+			p++;
+			break;
+		}
+
+	if (name) {
+		for(s=p; s<end; )
+			*name++ = *s++;
+
+		*name = '\0';
+	}
+
+	if (dir) {
+		for(s=path; s<p; )
+			*dir++ = *s++;
+
+		*dir = '\0';
+	}
+}
+
+/****************************************************************************
+ * WCMD_HandleTildaModifiers
+ *
+ * Handle the ~ modifiers when expanding %0-9 or (%a-z in for command)
+ *    %~xxxxxV  (V=0-9 or A-Z)
+ * Where xxxx is any combination of:
+ *    ~ - Removes quotes
+ *    f - Fully qualified path (assumes current dir if not drive\dir)
+ *    d - drive letter
+ *    p - path
+ *    n - filename
+ *    x - file extension
+ *    s - path with shortnames
+ *    a - attributes
+ *    t - date/time
+ *    z - size
+ *    $ENVVAR: - Searches ENVVAR for (contents of V) and expands to fully
+ *                   qualified path
+ *
+ *  To work out the length of the modifier:
+ *  
+ *  Note: In the case of %0-9 knowing the end of the modifier is easy,
+ *    but in a for loop, the for end character may also be a modifier
+ *    eg. for %a in (c:\a.a) do echo XXX
+ *             where XXX = %~a    (just ~)
+ *                         %~aa   (~ and attributes)
+ *                         %~aaxa (~, attributes and extension)
+ *                   BUT   %~aax  (~ and attributes followed by 'x')
+ *
+ *  Hence search forwards until find an invalid modifier, and then 
+ *  backwards until find for variable or 0-9                       
+ */
+void WCMD_HandleTildaModifiers(char **start, char *forVariable) {
+
+  #define NUMMODIFIERS 11
+  const char const validmodifiers[NUMMODIFIERS] = {
+        '~', 'f', 'd', 'p', 'n', 'x', 's', 'a', 't', 'z', '$'
+  };
+
+  WIN32_FILE_ATTRIBUTE_DATA fileInfo;
+  char  outputparam[MAX_PATH];
+  char  finaloutput[MAX_PATH];
+  char  fullfilename[MAX_PATH];
+  char  thisoutput[MAX_PATH];
+  char  *pos            = *start+1;
+  char  *firstModifier  = pos;
+  char  *lastModifier   = NULL;
+  int   modifierLen     = 0;
+  BOOL  finished        = FALSE;
+  int   i               = 0;
+  BOOL  exists          = TRUE;
+  BOOL  skipFileParsing = FALSE;
+  BOOL  doneModifier    = FALSE;
+
+  /* Search forwards until find invalid character modifier */
+  while (!finished) {
+
+    /* Work on the previous character */
+    if (lastModifier != NULL) {
+
+      for (i=0; i<NUMMODIFIERS; i++) {
+        if (validmodifiers[i] == *lastModifier) {
+
+          /* Special case '$' to skip until : found */
+          if (*lastModifier == '$') {
+            while (*pos != ':' && *pos) pos++;
+            if (*pos == 0x00) return; /* Invalid syntax */
+            pos++;                    /* Skip ':'       */
+          } 
+          break;
+        }
+      }
+
+      if (i==NUMMODIFIERS) {
+        finished = TRUE;
+      }
+    }
+
+    /* Save this one away */
+    if (!finished) {
+      lastModifier = pos;
+      pos++;
+    }
+  }
+
+  /* Now make sure the position we stopped at is a valid parameter */
+  if (!(*lastModifier >= '0' || *lastModifier <= '9') &&
+      (forVariable != NULL) && 
+      (toupper(*lastModifier) != toupper(*forVariable)))  {
+
+    /* Its not... Step backwards until it matches or we get to the start */
+    while (toupper(*lastModifier) != toupper(*forVariable) && 
+          lastModifier > firstModifier) {
+      lastModifier--;
+    }
+    if (lastModifier == firstModifier) return; /* Invalid syntax */
+  }
+
+  /* Extract the parameter to play with */
+  if ((*lastModifier >= '0' && *lastModifier <= '9')) {
+    strcpy(outputparam, WCMD_parameter (context -> command, 
+                 *lastModifier-'0' + context -> shift_count, NULL));
+  } else {
+    /* FIXME: Retrieve 'for' variable %c\n", *lastModifier); */
+    /* Need to get 'for' loop variable into outputparam      */
+    return;
+  }
+
+  /* So now, firstModifier points to beginning of modifiers, lastModifier 
+     points to the variable just after the modifiers. Process modifiers 
+     in a specific order, remembering there could be duplicates           */
+  modifierLen = lastModifier - firstModifier;
+  finaloutput[0] = 0x00;
+
+  /* Useful for debugging purposes: */
+  /*printf("Modifier string '%*.*s' and variable is %c\n Param starts as '%s'\n", 
+             (modifierLen), (modifierLen), firstModifier, *lastModifier, 
+             outputparam);*/
+
+  /* 1. Handle '~' : Strip surrounding quotes */
+  if (outputparam[0]=='"' &&
+      memchr(firstModifier, '~', modifierLen) != NULL) {
+    int len = strlen(outputparam);
+    if (outputparam[len-1] == '"') {
+        outputparam[len-1]=0x00;
+        len = len - 1;
+    }
+    memmove(outputparam, &outputparam[1], len-1);
+  }
+
+  /* 2. Handle the special case of a $ */
+  if (memchr(firstModifier, '$', modifierLen) != NULL) {
+    /* Special Case: Search envar specified in $[envvar] for outputparam 
+       Note both $ and : are guaranteed otherwise check above would fail */
+    char *start = strchr(firstModifier, '$') + 1;
+    char *end   = strchr(firstModifier, ':');
+    char env[MAX_PATH];
+    char fullpath[MAX_PATH];
+
+    /* Extract the env var */
+    strncpy(env, start, (end-start));
+    env[(end-start)] = 0x00;
+
+    /* If env var not found, return emptry string */
+    if ((GetEnvironmentVariable(env, fullpath, MAX_PATH) == 0) || 
+        (SearchPath(fullpath, outputparam, NULL, 
+                    MAX_PATH, outputparam, NULL) == 0)) {
+      finaloutput[0] = 0x00;
+      outputparam[0] = 0x00;
+      skipFileParsing = TRUE;
+    }
+  }
+
+  /* After this, we need full information on the file, 
+    which is valid not to exist.  */
+  if (!skipFileParsing) {
+    if (GetFullPathName(outputparam, MAX_PATH, fullfilename, NULL) == 0) 
+      return;
+
+    exists = GetFileAttributesExA(fullfilename, GetFileExInfoStandard, 
+                                  &fileInfo);
+  
+    /* 2. Handle 'a' : Output attributes */
+    if (exists && 
+        memchr(firstModifier, 'a', modifierLen) != NULL) {
+
+      doneModifier = TRUE;
+      strcpy(thisoutput, "---------");
+      if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)     
+        thisoutput[0]='d';
+      if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY)      
+        thisoutput[1]='r';
+      if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE)       
+        thisoutput[2]='a';
+      if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)        
+        thisoutput[3]='h';
+      if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM)        
+        thisoutput[4]='s';
+      if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED)    
+        thisoutput[5]='c';
+      /* FIXME: What are 6 and 7? */
+      if (fileInfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) 
+        thisoutput[8]='l';
+      strcat(finaloutput, thisoutput);
+    }
+  
+    /* 3. Handle 't' : Date+time */
+    if (exists && 
+        memchr(firstModifier, 't', modifierLen) != NULL) {
+
+      SYSTEMTIME systime;
+      int datelen;
+
+      doneModifier = TRUE;
+      if (finaloutput[0] != 0x00) strcat(finaloutput, " ");
+
+      /* Format the time */
+      FileTimeToSystemTime(&fileInfo.ftLastWriteTime, &systime);
+      GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &systime, 
+                        NULL, thisoutput, MAX_PATH); 
+      strcat(thisoutput, " ");
+      datelen = strlen(thisoutput);
+      GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &systime, 
+                        NULL, (thisoutput+datelen), MAX_PATH-datelen);
+      strcat(finaloutput, thisoutput);
+    }
+
+    /* 4. Handle 'z' : File length */
+    if (exists && 
+        memchr(firstModifier, 'z', modifierLen) != NULL) {
+      /* FIXME: Output full 64 bit size (sprintf not support I64 here) */
+      ULONG/*64*/ fullsize = /*(fileInfo.nFileSizeHigh << 32) +*/ 
+                                  fileInfo.nFileSizeLow;
+      
+      doneModifier = TRUE;
+      if (finaloutput[0] != 0x00) strcat(finaloutput, " ");
+      sprintf(thisoutput, "%u", fullsize);
+      strcat(finaloutput, thisoutput);
+    }
+
+    /* 4. Handle 's' : Use short paths (File doesnt have to exist) */
+    if (memchr(firstModifier, 's', modifierLen) != NULL) {
+      if (finaloutput[0] != 0x00) strcat(finaloutput, " ");
+      /* Dont flag as doneModifier - %~s on its own is processed later */
+      GetShortPathName(outputparam, outputparam, sizeof(outputparam));
+    }
+
+    /* 5. Handle 'f' : Fully qualified path (File doesnt have to exist) */
+    /*      Note this overrides d,p,n,x                                 */
+    if (memchr(firstModifier, 'f', modifierLen) != NULL) {
+      doneModifier = TRUE;
+      if (finaloutput[0] != 0x00) strcat(finaloutput, " ");
+      strcat(finaloutput, fullfilename);
+    } else {
+
+      char drive[10];
+      char dir[MAX_PATH];
+      char fname[MAX_PATH];
+      char ext[MAX_PATH];
+      BOOL doneFileModifier = FALSE;
+
+      if (finaloutput[0] != 0x00) strcat(finaloutput, " ");
+
+      /* Split into components */
+      _splitpath(fullfilename, drive, dir, fname, ext);
+
+      /* 5. Handle 'd' : Drive Letter */
+      if (memchr(firstModifier, 'd', modifierLen) != NULL) {
+        strcat(finaloutput, drive);
+        doneModifier = TRUE;
+        doneFileModifier = TRUE;
+      }
+
+      /* 6. Handle 'p' : Path */
+      if (memchr(firstModifier, 'p', modifierLen) != NULL) {
+        strcat(finaloutput, dir);
+        doneModifier = TRUE;
+        doneFileModifier = TRUE;
+      }
+
+      /* 7. Handle 'n' : Name */
+      if (memchr(firstModifier, 'n', modifierLen) != NULL) {
+        strcat(finaloutput, fname);
+        doneModifier = TRUE;
+        doneFileModifier = TRUE;
+      }
+
+      /* 8. Handle 'x' : Ext */
+      if (memchr(firstModifier, 'x', modifierLen) != NULL) {
+        strcat(finaloutput, ext);
+        doneModifier = TRUE;
+        doneFileModifier = TRUE;
+      }
+
+      /* If 's' but no other parameter, dump the whole thing */
+      if (!doneFileModifier && 
+          memchr(firstModifier, 's', modifierLen) != NULL) {
+        doneModifier = TRUE;
+        if (finaloutput[0] != 0x00) strcat(finaloutput, " ");
+        strcat(finaloutput, outputparam);
+      }
+    }
+  }
+
+  /* If No other modifier processed,  just add in parameter */
+  if (!doneModifier) strcpy(finaloutput, outputparam);
+
+  /* Finish by inserting the replacement into the string */
+  pos = strdup (lastModifier+1);
+  strcpy(*start, finaloutput);
+  strcat(*start, pos);
+  free(pos);
+}
+
-- 
1.3.0



More information about the wine-patches mailing list