mirror of
git://source.winehq.org/git/wine.git
synced 2024-07-21 12:24:10 +00:00
cmd: Add beginnings of support for delayed expansion.
This commit is contained in:
parent
3735bf6af5
commit
0b00b717b9
|
@ -386,7 +386,7 @@ void WCMD_splitpath(const WCHAR* path, WCHAR* drv, WCHAR* dir, WCHAR* name, WCHA
|
|||
* Hence search forwards until find an invalid modifier, and then
|
||||
* backwards until find for variable or 0-9
|
||||
*/
|
||||
void WCMD_HandleTildaModifiers(WCHAR **start, BOOL justFors)
|
||||
void WCMD_HandleTildaModifiers(WCHAR **start, BOOL atExecute)
|
||||
{
|
||||
|
||||
#define NUMMODIFIERS 11
|
||||
|
@ -444,7 +444,7 @@ void WCMD_HandleTildaModifiers(WCHAR **start, BOOL justFors)
|
|||
WINE_TRACE("Looking backwards for parameter id: %s\n",
|
||||
wine_dbgstr_w(lastModifier));
|
||||
|
||||
if (!justFors && context && (*lastModifier >= '0' && *lastModifier <= '9')) {
|
||||
if (!atExecute && context && (*lastModifier >= '0' && *lastModifier <= '9')) {
|
||||
/* Its a valid parameter identifier - OK */
|
||||
break;
|
||||
|
||||
|
|
|
@ -3150,12 +3150,30 @@ void WCMD_setlocal (const WCHAR *s) {
|
|||
WCHAR *env;
|
||||
struct env_stack *env_copy;
|
||||
WCHAR cwd[MAX_PATH];
|
||||
BOOL newdelay;
|
||||
static const WCHAR ondelayW[] = {'E','N','A','B','L','E','D','E','L','A',
|
||||
'Y','E','D','E','X','P','A','N','S','I',
|
||||
'O','N','\0'};
|
||||
static const WCHAR offdelayW[] = {'D','I','S','A','B','L','E','D','E','L',
|
||||
'A','Y','E','D','E','X','P','A','N','S',
|
||||
'I','O','N','\0'};
|
||||
|
||||
/* setlocal does nothing outside of batch programs */
|
||||
if (!context) return;
|
||||
|
||||
/* DISABLEEXTENSIONS ignored */
|
||||
|
||||
/* ENABLEDELAYEDEXPANSION / DISABLEDELAYEDEXPANSION could be parm1 or parm2
|
||||
(if both ENABLEEXTENSIONS and ENABLEDELAYEDEXPANSION supplied for example) */
|
||||
if (!strcmpiW(param1, ondelayW) || !strcmpiW(param2, ondelayW)) {
|
||||
newdelay = TRUE;
|
||||
} else if (!strcmpiW(param1, offdelayW) || !strcmpiW(param2, offdelayW)) {
|
||||
newdelay = FALSE;
|
||||
} else {
|
||||
newdelay = delayedsubst;
|
||||
}
|
||||
WINE_TRACE("Setting delayed expansion to %d\n", newdelay);
|
||||
|
||||
env_copy = LocalAlloc (LMEM_FIXED, sizeof (struct env_stack));
|
||||
if( !env_copy )
|
||||
{
|
||||
|
@ -3169,6 +3187,8 @@ void WCMD_setlocal (const WCHAR *s) {
|
|||
{
|
||||
env_copy->batchhandle = context->h;
|
||||
env_copy->next = saved_environment;
|
||||
env_copy->delayedsubst = delayedsubst;
|
||||
delayedsubst = newdelay;
|
||||
saved_environment = env_copy;
|
||||
|
||||
/* Save the current drive letter */
|
||||
|
@ -3226,6 +3246,8 @@ void WCMD_endlocal (void) {
|
|||
/* restore old environment */
|
||||
env = temp->strings;
|
||||
len = 0;
|
||||
delayedsubst = temp->delayedsubst;
|
||||
WINE_TRACE("Delayed expansion now %d\n", delayedsubst);
|
||||
while (env[len]) {
|
||||
n = strlenW(&env[len]) + 1;
|
||||
p = strchrW(&env[len] + 1, '=');
|
||||
|
|
|
@ -354,16 +354,16 @@ foo
|
|||
foo
|
||||
--- runtime (delayed) expansion mode
|
||||
foo
|
||||
@todo_wine@foo@or_broken@!WINE_FOO!
|
||||
foo@or_broken@!WINE_FOO!
|
||||
foo
|
||||
@todo_wine@bar@or_broken@foo
|
||||
bar@or_broken@foo
|
||||
0
|
||||
0@or_broken@1
|
||||
foo
|
||||
!WINE_FOO!
|
||||
--- using /V cmd flag
|
||||
foo
|
||||
@todo_wine@foo@or_broken@!WINE_FOO!
|
||||
foo@or_broken@!WINE_FOO!
|
||||
foo
|
||||
!WINE_FOO!
|
||||
------------ Testing conditional execution ------------
|
||||
|
|
|
@ -102,7 +102,7 @@ THIS FAILS: cmd ignoreme/c say one
|
|||
0@space@
|
||||
0@space@
|
||||
!@space@
|
||||
@todo_wine@0@space@@or_broken@!@space@
|
||||
0@space@@or_broken@!@space@
|
||||
@todo_wine@0@space@
|
||||
'@space@
|
||||
+@space@
|
||||
|
|
|
@ -112,7 +112,7 @@ WCHAR *WCMD_parameter_with_delims (WCHAR *s, int n, WCHAR **start, BOOL raw,
|
|||
BOOL wholecmdline, const WCHAR *delims);
|
||||
WCHAR *WCMD_skip_leading_spaces (WCHAR *string);
|
||||
BOOL WCMD_keyword_ws_found(const WCHAR *keyword, int len, const WCHAR *ptr);
|
||||
void WCMD_HandleTildaModifiers(WCHAR **start, BOOL justFors);
|
||||
void WCMD_HandleTildaModifiers(WCHAR **start, BOOL atExecute);
|
||||
|
||||
void WCMD_splitpath(const WCHAR* path, WCHAR* drv, WCHAR* dir, WCHAR* name, WCHAR* ext);
|
||||
void WCMD_strip_quotes(WCHAR *cmd);
|
||||
|
@ -171,6 +171,7 @@ struct env_stack
|
|||
} u;
|
||||
WCHAR *strings;
|
||||
HANDLE batchhandle; /* Used to ensure set/endlocals stay in scope */
|
||||
BOOL delayedsubst; /* Is delayed substitution in effect */
|
||||
};
|
||||
|
||||
/* Data structure to save setlocal and pushd information */
|
||||
|
@ -201,6 +202,7 @@ extern WCHAR quals[MAX_PATH], param1[MAXSTRING], param2[MAXSTRING];
|
|||
extern DWORD errorlevel;
|
||||
extern BATCH_CONTEXT *context;
|
||||
extern FOR_CONTEXT forloopcontext;
|
||||
extern BOOL delayedsubst;
|
||||
|
||||
#endif /* !RC_INVOKED */
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ DWORD errorlevel;
|
|||
WCHAR quals[MAX_PATH], param1[MAXSTRING], param2[MAXSTRING];
|
||||
BOOL interactive;
|
||||
FOR_CONTEXT forloopcontext; /* The 'for' loop context */
|
||||
BOOL delayedsubst = FALSE; /* The current delayed substitution setting */
|
||||
|
||||
int defaultColor = 7;
|
||||
BOOL echo_mode = TRUE;
|
||||
|
@ -548,7 +549,7 @@ static inline BOOL WCMD_is_magic_envvar(const WCHAR *s, const WCHAR *magicvar)
|
|||
*
|
||||
* Expands environment variables, allowing for WCHARacter substitution
|
||||
*/
|
||||
static WCHAR *WCMD_expand_envvar(WCHAR *start)
|
||||
static WCHAR *WCMD_expand_envvar(WCHAR *start, WCHAR startchar)
|
||||
{
|
||||
WCHAR *endOfVar = NULL, *s;
|
||||
WCHAR *colonpos = NULL;
|
||||
|
@ -562,11 +563,12 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start)
|
|||
static const WCHAR Time[] = {'T','I','M','E','\0'};
|
||||
static const WCHAR Cd[] = {'C','D','\0'};
|
||||
static const WCHAR Random[] = {'R','A','N','D','O','M','\0'};
|
||||
static const WCHAR Delims[] = {'%',':','\0'};
|
||||
WCHAR Delims[] = {'%',':','\0'}; /* First char gets replaced appropriately */
|
||||
|
||||
WINE_TRACE("Expanding: %s\n", wine_dbgstr_w(start));
|
||||
WINE_TRACE("Expanding: %s (%c)\n", wine_dbgstr_w(start), startchar);
|
||||
|
||||
/* Find the end of the environment variable, and extract name */
|
||||
Delims[0] = startchar;
|
||||
endOfVar = strpbrkW(start+1, Delims);
|
||||
|
||||
if (endOfVar == NULL || *endOfVar==' ') {
|
||||
|
@ -587,7 +589,7 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start)
|
|||
/* If ':' found, process remaining up until '%' (or stop at ':' if
|
||||
a missing '%' */
|
||||
if (*endOfVar==':') {
|
||||
WCHAR *endOfVar2 = strchrW(endOfVar+1, '%');
|
||||
WCHAR *endOfVar2 = strchrW(endOfVar+1, startchar);
|
||||
if (endOfVar2 != NULL) endOfVar = endOfVar2;
|
||||
}
|
||||
|
||||
|
@ -598,11 +600,18 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start)
|
|||
/* If there's complex substitution, just need %var% for now
|
||||
to get the expanded data to play with */
|
||||
if (colonpos) {
|
||||
*colonpos = '%';
|
||||
*colonpos = startchar;
|
||||
savedchar = *(colonpos+1);
|
||||
*(colonpos+1) = 0x00;
|
||||
}
|
||||
|
||||
/* By now, we know the variable we want to expand but it may be
|
||||
surrounded by '!' if we are in delayed expansion - if so convert
|
||||
to % signs. */
|
||||
if (startchar=='!') {
|
||||
thisVar[0] = '%';
|
||||
thisVar[(endOfVar - start)] = '%';
|
||||
}
|
||||
WINE_TRACE("Retrieving contents of %s\n", wine_dbgstr_w(thisVar));
|
||||
|
||||
/* Expand to contents, if unchanged, return */
|
||||
|
@ -788,8 +797,11 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start)
|
|||
* Expand the command. Native expands lines from batch programs as they are
|
||||
* read in and not again, except for 'for' variable substitution.
|
||||
* eg. As evidence, "echo %1 && shift && echo %1" or "echo %%path%%"
|
||||
* atExecute is TRUE when the expansion is occuring as the command is executed
|
||||
* rather than at parse time, ie delayed expansion and for loops need to be
|
||||
* processed
|
||||
*/
|
||||
static void handleExpansion(WCHAR *cmd, BOOL justFors) {
|
||||
static void handleExpansion(WCHAR *cmd, BOOL atExecute, BOOL delayed) {
|
||||
|
||||
/* For commands in a context (batch program): */
|
||||
/* Expand environment variables in a batch file %{0-9} first */
|
||||
|
@ -803,6 +815,9 @@ static void handleExpansion(WCHAR *cmd, BOOL justFors) {
|
|||
WCHAR *p = cmd;
|
||||
WCHAR *t;
|
||||
int i;
|
||||
WCHAR *delayedp = NULL;
|
||||
WCHAR startchar = '%';
|
||||
WCHAR *normalp;
|
||||
|
||||
/* Display the FOR variables in effect */
|
||||
for (i=0;i<52;i++) {
|
||||
|
@ -813,14 +828,22 @@ static void handleExpansion(WCHAR *cmd, BOOL justFors) {
|
|||
}
|
||||
}
|
||||
|
||||
while ((p = strchrW(p, '%'))) {
|
||||
/* Find the next environment variable delimiter */
|
||||
normalp = strchrW(p, '%');
|
||||
if (delayed) delayedp = strchrW(p, '!');
|
||||
if (!normalp) p = delayedp;
|
||||
else if (!delayedp) p = normalp;
|
||||
else p = min(p,delayedp);
|
||||
if (p) startchar = *p;
|
||||
|
||||
while (p) {
|
||||
|
||||
WINE_TRACE("Translate command:%s %d (at: %s)\n",
|
||||
wine_dbgstr_w(cmd), justFors, wine_dbgstr_w(p));
|
||||
wine_dbgstr_w(cmd), atExecute, wine_dbgstr_w(p));
|
||||
i = *(p+1) - '0';
|
||||
|
||||
/* Don't touch %% unless its in Batch */
|
||||
if (!justFors && *(p+1) == '%') {
|
||||
if (!atExecute && *(p+1) == startchar) {
|
||||
if (context) {
|
||||
WCMD_strsubstW(p, p+1, NULL, 0);
|
||||
}
|
||||
|
@ -828,17 +851,17 @@ static void handleExpansion(WCHAR *cmd, BOOL justFors) {
|
|||
|
||||
/* Replace %~ modifications if in batch program */
|
||||
} else if (*(p+1) == '~') {
|
||||
WCMD_HandleTildaModifiers(&p, justFors);
|
||||
WCMD_HandleTildaModifiers(&p, atExecute);
|
||||
p++;
|
||||
|
||||
/* Replace use of %0...%9 if in batch program*/
|
||||
} else if (!justFors && context && (i >= 0) && (i <= 9)) {
|
||||
} else if (!atExecute && context && (i >= 0) && (i <= 9) && startchar == '%') {
|
||||
t = WCMD_parameter(context -> command, i + context -> shift_count[i],
|
||||
NULL, TRUE, TRUE);
|
||||
WCMD_strsubstW(p, p+2, t, -1);
|
||||
|
||||
/* Replace use of %* if in batch program*/
|
||||
} else if (!justFors && context && *(p+1)=='*') {
|
||||
} else if (!atExecute && context && *(p+1)=='*' && startchar == '%') {
|
||||
WCHAR *startOfParms = NULL;
|
||||
WCHAR *thisParm = WCMD_parameter(context -> command, 0, &startOfParms, TRUE, TRUE);
|
||||
if (startOfParms != NULL) {
|
||||
|
@ -850,17 +873,25 @@ static void handleExpansion(WCHAR *cmd, BOOL justFors) {
|
|||
|
||||
} else {
|
||||
int forvaridx = FOR_VAR_IDX(*(p+1));
|
||||
if (forvaridx != -1 && forloopcontext.variable[forvaridx]) {
|
||||
if (startchar == '%' && forvaridx != -1 && forloopcontext.variable[forvaridx]) {
|
||||
/* Replace the 2 characters, % and for variable character */
|
||||
WCMD_strsubstW(p, p + 2, forloopcontext.variable[forvaridx], -1);
|
||||
} else if (!justFors) {
|
||||
p = WCMD_expand_envvar(p);
|
||||
} else if (!atExecute || (atExecute && startchar == '!')) {
|
||||
p = WCMD_expand_envvar(p, startchar);
|
||||
|
||||
/* In a FOR loop, see if this is the variable to replace */
|
||||
} else { /* Ignore %'s on second pass of batch program */
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
/* Find the next environment variable delimiter */
|
||||
normalp = strchrW(p, '%');
|
||||
if (delayed) delayedp = strchrW(p, '!');
|
||||
if (!normalp) p = delayedp;
|
||||
else if (!delayedp) p = normalp;
|
||||
else p = min(p,delayedp);
|
||||
if (p) startchar = *p;
|
||||
}
|
||||
|
||||
return;
|
||||
|
@ -1303,8 +1334,8 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
|
|||
|
||||
/* Expand variables in command line mode only (batch mode will
|
||||
be expanded as the line is read in, except for 'for' loops) */
|
||||
handleExpansion(new_cmd, (context != NULL));
|
||||
handleExpansion(new_redir, (context != NULL));
|
||||
handleExpansion(new_cmd, (context != NULL), delayedsubst);
|
||||
handleExpansion(new_redir, (context != NULL), delayedsubst);
|
||||
cmd = new_cmd;
|
||||
|
||||
/*
|
||||
|
@ -1825,7 +1856,7 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE
|
|||
}
|
||||
|
||||
/* Replace env vars if in a batch context */
|
||||
if (context) handleExpansion(extraSpace, FALSE);
|
||||
if (context) handleExpansion(extraSpace, FALSE, FALSE);
|
||||
|
||||
/* Skip preceding whitespace */
|
||||
while (*curPos == ' ' || *curPos == '\t') curPos++;
|
||||
|
@ -2222,7 +2253,7 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE
|
|||
|
||||
} while (*extraData == 0x00);
|
||||
curPos = extraSpace;
|
||||
if (context) handleExpansion(extraSpace, FALSE);
|
||||
if (context) handleExpansion(extraSpace, FALSE, FALSE);
|
||||
/* Continue to echo commands IF echo is on and in batch program */
|
||||
if (context && echo_mode && extraSpace[0] && (extraSpace[0] != '@')) {
|
||||
WCMD_output_asis(extraSpace);
|
||||
|
@ -2313,6 +2344,7 @@ int wmain (int argc, WCHAR *argvW[])
|
|||
WCHAR envvar[4];
|
||||
BOOL opt_q;
|
||||
int opt_t = 0;
|
||||
static const WCHAR offW[] = {'O','F','F','\0'};
|
||||
static const WCHAR promptW[] = {'P','R','O','M','P','T','\0'};
|
||||
static const WCHAR defaultpromptW[] = {'$','P','$','G','\0'};
|
||||
CMD_LIST *toExecute = NULL; /* Commands left to be executed */
|
||||
|
@ -2366,13 +2398,17 @@ int wmain (int argc, WCHAR *argvW[])
|
|||
unicodeOutput = FALSE;
|
||||
} else if (tolowerW(c)=='u') {
|
||||
unicodeOutput = TRUE;
|
||||
} else if (tolowerW(c)=='v' && argPos[2]==':') {
|
||||
delayedsubst = strncmpiW(&argPos[3], offW, 3);
|
||||
if (delayedsubst) WINE_TRACE("Delayed substitution is on\n");
|
||||
} else if (tolowerW(c)=='t' && argPos[2]==':') {
|
||||
opt_t=strtoulW(&argPos[3], NULL, 16);
|
||||
} else if (tolowerW(c)=='x' || tolowerW(c)=='y') {
|
||||
/* Ignored for compatibility with Windows */
|
||||
}
|
||||
|
||||
if (argPos[2]==0 || argPos[2]==' ' || argPos[2]=='\t') {
|
||||
if (argPos[2]==0 || argPos[2]==' ' || argPos[2]=='\t' ||
|
||||
tolowerW(c)=='v') {
|
||||
args++;
|
||||
WCMD_parameter(cmdLine, args, &argPos, TRUE, TRUE);
|
||||
}
|
||||
|
@ -2387,8 +2423,7 @@ int wmain (int argc, WCHAR *argvW[])
|
|||
}
|
||||
|
||||
if (opt_q) {
|
||||
static const WCHAR eoff[] = {'O','F','F','\0'};
|
||||
WCMD_echo(eoff);
|
||||
WCMD_echo(offW);
|
||||
}
|
||||
|
||||
/* Until we start to read from the keyboard, stay as non-interactive */
|
||||
|
|
Loading…
Reference in a new issue