mirror of
https://github.com/reactos/reactos.git
synced 2025-08-05 08:13:00 +00:00
Implement SETLOCAL and ENDLOCAL commands. Make delayed expansion optional (disabled by default, enabled by CMD /V switch or with SETLOCAL)
svn path=/trunk/; revision=39892
This commit is contained in:
parent
64455e3b04
commit
fff257c059
5 changed files with 117 additions and 10 deletions
|
@ -189,6 +189,9 @@ VOID ExitBatch (LPTSTR msg)
|
||||||
/* Preserve echo state across batch calls */
|
/* Preserve echo state across batch calls */
|
||||||
bEcho = bc->bEcho;
|
bEcho = bc->bEcho;
|
||||||
|
|
||||||
|
while (bc->setlocal)
|
||||||
|
cmd_endlocal(_T(""));
|
||||||
|
|
||||||
bc = bc->prev;
|
bc = bc->prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -239,16 +242,23 @@ BOOL Batch (LPTSTR fullname, LPTSTR firstword, LPTSTR param, PARSED_COMMAND *Cmd
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
struct _SETLOCAL *setlocal = NULL;
|
||||||
/* If a batch file runs another batch file as part of a compound command
|
/* If a batch file runs another batch file as part of a compound command
|
||||||
* (e.g. "x.bat & somethingelse") then the first file gets terminated. */
|
* (e.g. "x.bat & somethingelse") then the first file gets terminated. */
|
||||||
if (Cmd != NULL)
|
if (bc && Cmd != NULL)
|
||||||
|
{
|
||||||
|
/* Get its SETLOCAL stack so it can be migrated to the new context */
|
||||||
|
setlocal = bc->setlocal;
|
||||||
|
bc->setlocal = NULL;
|
||||||
ExitBatch(NULL);
|
ExitBatch(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
/* Create a new context. This function will not
|
/* Create a new context. This function will not
|
||||||
* return until this context has been exited */
|
* return until this context has been exited */
|
||||||
new.prev = bc;
|
new.prev = bc;
|
||||||
bc = &new;
|
bc = &new;
|
||||||
bc->RedirList = NULL;
|
bc->RedirList = NULL;
|
||||||
|
bc->setlocal = setlocal;
|
||||||
}
|
}
|
||||||
|
|
||||||
GetFullPathName(fullname, sizeof(bc->BatchFilePath) / sizeof(TCHAR), bc->BatchFilePath, NULL);
|
GetFullPathName(fullname, sizeof(bc->BatchFilePath) / sizeof(TCHAR), bc->BatchFilePath, NULL);
|
||||||
|
|
|
@ -18,6 +18,7 @@ typedef struct tagBATCHCONTEXT
|
||||||
BOOL bEcho; /* Preserve echo flag across batch calls */
|
BOOL bEcho; /* Preserve echo flag across batch calls */
|
||||||
REDIRECTION *RedirList;
|
REDIRECTION *RedirList;
|
||||||
PARSED_COMMAND *current;
|
PARSED_COMMAND *current;
|
||||||
|
struct _SETLOCAL *setlocal;
|
||||||
} BATCH_CONTEXT, *LPBATCH_CONTEXT;
|
} BATCH_CONTEXT, *LPBATCH_CONTEXT;
|
||||||
|
|
||||||
typedef struct tagFORCONTEXT
|
typedef struct tagFORCONTEXT
|
||||||
|
|
|
@ -157,6 +157,7 @@ BOOL bCtrlBreak = FALSE; /* Ctrl-Break or Ctrl-C hit */
|
||||||
BOOL bIgnoreEcho = FALSE; /* Set this to TRUE to prevent a newline, when executing a command */
|
BOOL bIgnoreEcho = FALSE; /* Set this to TRUE to prevent a newline, when executing a command */
|
||||||
INT nErrorLevel = 0; /* Errorlevel of last launched external program */
|
INT nErrorLevel = 0; /* Errorlevel of last launched external program */
|
||||||
BOOL bChildProcessRunning = FALSE;
|
BOOL bChildProcessRunning = FALSE;
|
||||||
|
BOOL bDelayedExpansion = FALSE;
|
||||||
DWORD dwChildProcessId = 0;
|
DWORD dwChildProcessId = 0;
|
||||||
OSVERSIONINFO osvi;
|
OSVERSIONINFO osvi;
|
||||||
HANDLE hIn;
|
HANDLE hIn;
|
||||||
|
@ -1295,7 +1296,7 @@ DoDelayedExpansion(LPTSTR Line)
|
||||||
if (!SubstituteForVars(Line, Buf1))
|
if (!SubstituteForVars(Line, Buf1))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (!_tcschr(Buf1, _T('!')))
|
if (!bDelayedExpansion || !_tcschr(Buf1, _T('!')))
|
||||||
return cmd_dup(Buf1);
|
return cmd_dup(Buf1);
|
||||||
|
|
||||||
/* FIXME: Delayed substitutions actually aren't quite the same as
|
/* FIXME: Delayed substitutions actually aren't quite the same as
|
||||||
|
@ -1663,6 +1664,10 @@ Initialize()
|
||||||
SetScreenColor (wColor, TRUE);
|
SetScreenColor (wColor, TRUE);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
else if (_totlower(ptr[1]) == _T('v'))
|
||||||
|
{
|
||||||
|
bDelayedExpansion = _tcsnicmp(&ptr[2], _T(":off"), 4);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -59,6 +59,7 @@ extern WORD wDefColor;
|
||||||
extern BOOL bCtrlBreak;
|
extern BOOL bCtrlBreak;
|
||||||
extern BOOL bIgnoreEcho;
|
extern BOOL bIgnoreEcho;
|
||||||
extern BOOL bExit;
|
extern BOOL bExit;
|
||||||
|
extern BOOL bDelayedExpansion;
|
||||||
extern INT nErrorLevel;
|
extern INT nErrorLevel;
|
||||||
extern SHORT maxx;
|
extern SHORT maxx;
|
||||||
extern SHORT maxy;
|
extern SHORT maxy;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* GOTO.C - goto internal batch command.
|
* SETLOCAL.C - setlocal and endlocal internal batch commands.
|
||||||
*
|
*
|
||||||
* History:
|
* History:
|
||||||
*
|
*
|
||||||
|
@ -9,19 +9,109 @@
|
||||||
|
|
||||||
#include <precomp.h>
|
#include <precomp.h>
|
||||||
|
|
||||||
|
typedef struct _SETLOCAL {
|
||||||
|
struct _SETLOCAL *Prev;
|
||||||
|
BOOL DelayedExpansion;
|
||||||
|
LPTSTR Environment;
|
||||||
|
} SETLOCAL;
|
||||||
|
|
||||||
/* unimplemented */
|
/* Create a copy of the current environment */
|
||||||
|
static LPTSTR DuplicateEnvironment()
|
||||||
/* our current default is delayedexpansion */
|
|
||||||
|
|
||||||
INT cmd_setlocal (LPTSTR param)
|
|
||||||
{
|
{
|
||||||
return 0;
|
LPTSTR Environ = GetEnvironmentStrings();
|
||||||
|
LPTSTR End, EnvironCopy;
|
||||||
|
if (!Environ)
|
||||||
|
return NULL;
|
||||||
|
for (End = Environ; *End; End += _tcslen(End) + 1)
|
||||||
|
;
|
||||||
|
EnvironCopy = cmd_alloc((End + 1 - Environ) * sizeof(TCHAR));
|
||||||
|
if (EnvironCopy)
|
||||||
|
memcpy(EnvironCopy, Environ, (End + 1 - Environ) * sizeof(TCHAR));
|
||||||
|
FreeEnvironmentStrings(Environ);
|
||||||
|
return EnvironCopy;
|
||||||
|
}
|
||||||
|
|
||||||
|
INT cmd_setlocal(LPTSTR param)
|
||||||
|
{
|
||||||
|
SETLOCAL *Saved;
|
||||||
|
|
||||||
|
/* SETLOCAL only works inside a batch file */
|
||||||
|
if (!bc)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
Saved = cmd_alloc(sizeof(SETLOCAL));
|
||||||
|
if (!Saved)
|
||||||
|
{
|
||||||
|
error_out_of_memory();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
Saved->Prev = bc->setlocal;
|
||||||
|
Saved->DelayedExpansion = bDelayedExpansion;
|
||||||
|
Saved->Environment = DuplicateEnvironment();
|
||||||
|
if (!Saved->Environment)
|
||||||
|
{
|
||||||
|
error_out_of_memory();
|
||||||
|
cmd_free(Saved);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
bc->setlocal = Saved;
|
||||||
|
|
||||||
|
nErrorLevel = 0;
|
||||||
|
|
||||||
|
if (*param == _T('\0'))
|
||||||
|
/* nothing */;
|
||||||
|
else if (!_tcsicmp(param, _T("enabledelayedexpansion")))
|
||||||
|
bDelayedExpansion = TRUE;
|
||||||
|
else if (!_tcsicmp(param, _T("disabledelayedexpansion")))
|
||||||
|
bDelayedExpansion = FALSE;
|
||||||
|
else
|
||||||
|
error_invalid_parameter_format(param);
|
||||||
|
|
||||||
|
return nErrorLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* endlocal doesn't take any params */
|
/* endlocal doesn't take any params */
|
||||||
INT cmd_endlocal (LPTSTR param)
|
INT cmd_endlocal(LPTSTR param)
|
||||||
{
|
{
|
||||||
|
LPTSTR Environ, Name, Value;
|
||||||
|
SETLOCAL *Saved;
|
||||||
|
|
||||||
|
/* Pop a SETLOCAL struct off of this batch file's stack */
|
||||||
|
if (!bc || !(Saved = bc->setlocal))
|
||||||
|
return 0;
|
||||||
|
bc->setlocal = Saved->Prev;
|
||||||
|
|
||||||
|
bDelayedExpansion = Saved->DelayedExpansion;
|
||||||
|
|
||||||
|
/* First, clear out the environment. Since making any changes to the
|
||||||
|
* environment invalidates pointers obtained from GetEnvironmentStrings(),
|
||||||
|
* we must make a copy of it and get the variable names from that */
|
||||||
|
Environ = DuplicateEnvironment();
|
||||||
|
if (Environ)
|
||||||
|
{
|
||||||
|
for (Name = Environ; *Name; Name += _tcslen(Name) + 1)
|
||||||
|
{
|
||||||
|
if (!(Value = _tcschr(Name + 1, _T('='))))
|
||||||
|
continue;
|
||||||
|
*Value++ = _T('\0');
|
||||||
|
SetEnvironmentVariable(Name, NULL);
|
||||||
|
Name = Value;
|
||||||
|
}
|
||||||
|
cmd_free(Environ);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now, restore variables from the copy saved by cmd_setlocal */
|
||||||
|
for (Name = Saved->Environment; *Name; Name += _tcslen(Name) + 1)
|
||||||
|
{
|
||||||
|
if (!(Value = _tcschr(Name + 1, _T('='))))
|
||||||
|
continue;
|
||||||
|
*Value++ = _T('\0');
|
||||||
|
SetEnvironmentVariable(Name, Value);
|
||||||
|
Name = Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd_free(Saved->Environment);
|
||||||
|
cmd_free(Saved);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue