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:
Jeffrey Morlan 2009-03-06 18:05:45 +00:00
parent 64455e3b04
commit fff257c059
5 changed files with 117 additions and 10 deletions

View file

@ -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);

View file

@ -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

View file

@ -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);
}
}
}

View file

@ -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;

View file

@ -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;
}