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=3…
==============================================================================
--- 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=3…
==============================================================================
--- 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?…
==============================================================================
--- 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;
-}
-