mirror of
https://github.com/reactos/reactos.git
synced 2025-02-23 08:55:19 +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 */
|
||||
bEcho = bc->bEcho;
|
||||
|
||||
while (bc->setlocal)
|
||||
cmd_endlocal(_T(""));
|
||||
|
||||
bc = bc->prev;
|
||||
}
|
||||
|
||||
|
@ -239,16 +242,23 @@ BOOL Batch (LPTSTR fullname, LPTSTR firstword, LPTSTR param, PARSED_COMMAND *Cmd
|
|||
}
|
||||
else
|
||||
{
|
||||
struct _SETLOCAL *setlocal = NULL;
|
||||
/* 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. */
|
||||
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);
|
||||
}
|
||||
|
||||
/* Create a new context. This function will not
|
||||
* return until this context has been exited */
|
||||
new.prev = bc;
|
||||
bc = &new;
|
||||
bc->RedirList = NULL;
|
||||
bc->setlocal = setlocal;
|
||||
}
|
||||
|
||||
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 */
|
||||
REDIRECTION *RedirList;
|
||||
PARSED_COMMAND *current;
|
||||
struct _SETLOCAL *setlocal;
|
||||
} BATCH_CONTEXT, *LPBATCH_CONTEXT;
|
||||
|
||||
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 */
|
||||
INT nErrorLevel = 0; /* Errorlevel of last launched external program */
|
||||
BOOL bChildProcessRunning = FALSE;
|
||||
BOOL bDelayedExpansion = FALSE;
|
||||
DWORD dwChildProcessId = 0;
|
||||
OSVERSIONINFO osvi;
|
||||
HANDLE hIn;
|
||||
|
@ -1295,7 +1296,7 @@ DoDelayedExpansion(LPTSTR Line)
|
|||
if (!SubstituteForVars(Line, Buf1))
|
||||
return NULL;
|
||||
|
||||
if (!_tcschr(Buf1, _T('!')))
|
||||
if (!bDelayedExpansion || !_tcschr(Buf1, _T('!')))
|
||||
return cmd_dup(Buf1);
|
||||
|
||||
/* FIXME: Delayed substitutions actually aren't quite the same as
|
||||
|
@ -1663,6 +1664,10 @@ Initialize()
|
|||
SetScreenColor (wColor, TRUE);
|
||||
}
|
||||
#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 bIgnoreEcho;
|
||||
extern BOOL bExit;
|
||||
extern BOOL bDelayedExpansion;
|
||||
extern INT nErrorLevel;
|
||||
extern SHORT maxx;
|
||||
extern SHORT maxy;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* GOTO.C - goto internal batch command.
|
||||
* SETLOCAL.C - setlocal and endlocal internal batch commands.
|
||||
*
|
||||
* History:
|
||||
*
|
||||
|
@ -9,19 +9,109 @@
|
|||
|
||||
#include <precomp.h>
|
||||
|
||||
typedef struct _SETLOCAL {
|
||||
struct _SETLOCAL *Prev;
|
||||
BOOL DelayedExpansion;
|
||||
LPTSTR Environment;
|
||||
} SETLOCAL;
|
||||
|
||||
/* unimplemented */
|
||||
|
||||
/* our current default is delayedexpansion */
|
||||
|
||||
INT cmd_setlocal (LPTSTR param)
|
||||
/* Create a copy of the current environment */
|
||||
static LPTSTR DuplicateEnvironment()
|
||||
{
|
||||
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 */
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue