Author: jmorlan Date: Fri Mar 6 21:05:45 2009 New Revision: 39892
URL: http://svn.reactos.org/svn/reactos?rev=39892&view=rev Log: Implement SETLOCAL and ENDLOCAL commands. Make delayed expansion optional (disabled by default, enabled by CMD /V switch or with SETLOCAL)
Modified: trunk/reactos/base/shell/cmd/batch.c trunk/reactos/base/shell/cmd/batch.h trunk/reactos/base/shell/cmd/cmd.c trunk/reactos/base/shell/cmd/cmd.h trunk/reactos/base/shell/cmd/setlocal.c
Modified: trunk/reactos/base/shell/cmd/batch.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/base/shell/cmd/batch.c?rev=... ============================================================================== --- trunk/reactos/base/shell/cmd/batch.c [iso-8859-1] (original) +++ trunk/reactos/base/shell/cmd/batch.c [iso-8859-1] Fri Mar 6 21:05:45 2009 @@ -189,6 +189,9 @@ /* Preserve echo state across batch calls */ bEcho = bc->bEcho;
+ while (bc->setlocal) + cmd_endlocal(_T("")); + bc = bc->prev; }
@@ -239,16 +242,23 @@ } 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);
Modified: trunk/reactos/base/shell/cmd/batch.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/base/shell/cmd/batch.h?rev=... ============================================================================== --- trunk/reactos/base/shell/cmd/batch.h [iso-8859-1] (original) +++ trunk/reactos/base/shell/cmd/batch.h [iso-8859-1] Fri Mar 6 21:05:45 2009 @@ -18,6 +18,7 @@ BOOL bEcho; /* Preserve echo flag across batch calls */ REDIRECTION *RedirList; PARSED_COMMAND *current; + struct _SETLOCAL *setlocal; } BATCH_CONTEXT, *LPBATCH_CONTEXT;
typedef struct tagFORCONTEXT
Modified: trunk/reactos/base/shell/cmd/cmd.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/base/shell/cmd/cmd.c?rev=39... ============================================================================== --- trunk/reactos/base/shell/cmd/cmd.c [iso-8859-1] (original) +++ trunk/reactos/base/shell/cmd/cmd.c [iso-8859-1] Fri Mar 6 21:05:45 2009 @@ -157,6 +157,7 @@ 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 @@ 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 @@ SetScreenColor (wColor, TRUE); } #endif + else if (_totlower(ptr[1]) == _T('v')) + { + bDelayedExpansion = _tcsnicmp(&ptr[2], _T(":off"), 4); + } } }
Modified: trunk/reactos/base/shell/cmd/cmd.h URL: http://svn.reactos.org/svn/reactos/trunk/reactos/base/shell/cmd/cmd.h?rev=39... ============================================================================== --- trunk/reactos/base/shell/cmd/cmd.h [iso-8859-1] (original) +++ trunk/reactos/base/shell/cmd/cmd.h [iso-8859-1] Fri Mar 6 21:05:45 2009 @@ -59,6 +59,7 @@ extern BOOL bCtrlBreak; extern BOOL bIgnoreEcho; extern BOOL bExit; +extern BOOL bDelayedExpansion; extern INT nErrorLevel; extern SHORT maxx; extern SHORT maxy;
Modified: trunk/reactos/base/shell/cmd/setlocal.c URL: http://svn.reactos.org/svn/reactos/trunk/reactos/base/shell/cmd/setlocal.c?r... ============================================================================== --- trunk/reactos/base/shell/cmd/setlocal.c [iso-8859-1] (original) +++ trunk/reactos/base/shell/cmd/setlocal.c [iso-8859-1] Fri Mar 6 21:05:45 2009 @@ -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 */ +/* Create a copy of the current environment */ +static LPTSTR DuplicateEnvironment() +{ + 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; +}
-/* our current default is delayedexpansion */ +INT cmd_setlocal(LPTSTR param) +{ + SETLOCAL *Saved;
-INT cmd_setlocal (LPTSTR param) + /* 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) { + 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; }
-/* endlocal doesn't take any params */ -INT cmd_endlocal (LPTSTR param) -{ - return 0; -} -