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 */ /* 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);

View file

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

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 */ 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);
}
} }
} }

View file

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

View file

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