cmd.exe: Unify parsing and expansion.

This commit is contained in:
Jason Edmeades 2007-02-23 22:21:27 +00:00 committed by Alexandre Julliard
parent 327d7ef9e6
commit 98ae8a6941
3 changed files with 125 additions and 149 deletions

View File

@ -20,17 +20,11 @@
#include "wcmd.h" #include "wcmd.h"
void WCMD_batch_command (char *line);
void WCMD_HandleTildaModifiers(char **start, char *forVariable);
extern int echo_mode; extern int echo_mode;
extern char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH]; extern char quals[MAX_PATH], param1[MAX_PATH], param2[MAX_PATH];
extern BATCH_CONTEXT *context; extern BATCH_CONTEXT *context;
extern DWORD errorlevel; extern DWORD errorlevel;
/* msdn specified max for Win XP */
#define MAXSTRING 8192
/**************************************************************************** /****************************************************************************
* WCMD_batch * WCMD_batch
* *
@ -117,7 +111,7 @@ BATCH_CONTEXT *prev_context;
WCMD_output_asis( "\n"); WCMD_output_asis( "\n");
} }
if (string[0] != ':') { /* Skip over labels */ if (string[0] != ':') { /* Skip over labels */
WCMD_batch_command (string); WCMD_process_command (string);
} }
} }
CloseHandle (h); 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. * WCMD_parameter - extract a parameter from a command line.
* *

View File

@ -79,6 +79,8 @@ char *WCMD_parameter (char *s, int n, char **where);
char *WCMD_strtrim_leading_spaces (char *string); char *WCMD_strtrim_leading_spaces (char *string);
void WCMD_strtrim_trailing_spaces (char *string); void WCMD_strtrim_trailing_spaces (char *string);
void WCMD_opt_s_strip_quotes(char *cmd); void WCMD_opt_s_strip_quotes(char *cmd);
void WCMD_HandleTildaModifiers(char **start, char *forVariable);
/* Data structure to hold context when executing batch files */ /* Data structure to hold context when executing batch files */
@ -151,3 +153,6 @@ extern const char nyi[];
extern const char newline[]; extern const char newline[];
extern const char version_string[]; extern const char version_string[];
extern const char anykey[]; extern const char anykey[];
/* msdn specified max for Win XP */
#define MAXSTRING 8192

View File

@ -301,27 +301,104 @@ int main (int argc, char *argv[])
void WCMD_process_command (char *command) void WCMD_process_command (char *command)
{ {
char *cmd, *p, *s; char *cmd, *p, *s, *t;
char temp[MAXSTRING];
int status, i, len; int status, i, len;
DWORD count, creationDisposition; DWORD count, creationDisposition;
HANDLE h; HANDLE h;
char *whichcmd; char *whichcmd;
SECURITY_ATTRIBUTES sa; SECURITY_ATTRIBUTES sa;
char *new_cmd;
/* /* Move copy of the command onto the heap so it can be expanded */
* Replace errorlevel with current value (This shrinks in new_cmd = HeapAlloc( GetProcessHeap(), 0, MAXSTRING );
* place and hence no need to reallocate the memory yet) strcpy(new_cmd, command);
*/
p = 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, '%'))) { while ((p = strchr(p, '%'))) {
if (CompareString (LOCALE_USER_DEFAULT, i = *(p+1) - '0';
NORM_IGNORECASE | SORT_STRINGSORT,
(p+1), 11, "ERRORLEVEL%", -1) == 2) { /* Replace %~ modifications if in batch program */
char output[10]; if (context && *(p+1) == '~') {
sprintf(output, "%d", errorlevel); WCMD_HandleTildaModifiers(&p, NULL);
s = strdup (p+12); p++;
strcpy (p, output);
/* 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); 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) &&
(GetEnvironmentVariable("ERRORLEVEL", temp, 1) == 0) &&
(GetLastError() == ERROR_ENVVAR_NOT_FOUND)) {
sprintf(temp, "%d", errorlevel);
s = strdup (p+12);
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 { } else {
p++; p++;
} }
@ -329,14 +406,41 @@ void WCMD_process_command (char *command)
/* /*
* Expand up environment variables. * 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 ); cmd = HeapAlloc( GetProcessHeap(), 0, len );
status = ExpandEnvironmentStrings (command, cmd, len); status = ExpandEnvironmentStrings (new_cmd, cmd, len);
if (!status) { if (!status) {
WCMD_print_error (); WCMD_print_error ();
HeapFree( GetProcessHeap(), 0, cmd ); HeapFree( GetProcessHeap(), 0, cmd );
HeapFree( GetProcessHeap(), 0, new_cmd );
return; 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");
}
} }
/* /*