cmd.exe: Unify parsing and expansion.

oldstable
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"
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.
*

View File

@ -79,6 +79,8 @@ char *WCMD_parameter (char *s, int n, char **where);
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 */
@ -151,3 +153,6 @@ 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

View File

@ -301,27 +301,104 @@ int main (int argc, char *argv[])
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;
char *new_cmd;
/*
* Replace errorlevel with current value (This shrinks in
* place and hence no need to reallocate the memory yet)
*/
p = command;
/* 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,
NORM_IGNORECASE | SORT_STRINGSORT,
(p+1), 11, "ERRORLEVEL%", -1) == 2) {
char output[10];
sprintf(output, "%d", errorlevel);
s = strdup (p+12);
strcpy (p, output);
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) &&
(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 {
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");
}
}
/*