[05/16] CMD.exe: Add ~ modifiers for %0-9
Ann & Jason Edmeades
us at edmeades.me.uk
Mon Mar 26 05:17:39 CDT 2007
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