[ros-diffs] [jmorlan] 39892: Implement SETLOCAL and ENDLOCAL commands. Make delayed expansion optional (disabled by default, enabled by CMD /V switch or with SETLOCAL)

jmorlan at svn.reactos.org jmorlan at svn.reactos.org
Fri Mar 6 19:05:46 CET 2009


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=39892&r1=39891&r2=39892&view=diff
==============================================================================
--- 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=39892&r1=39891&r2=39892&view=diff
==============================================================================
--- 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=39892&r1=39891&r2=39892&view=diff
==============================================================================
--- 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=39892&r1=39891&r2=39892&view=diff
==============================================================================
--- 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?rev=39892&r1=39891&r2=39892&view=diff
==============================================================================
--- 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;
-}
-



More information about the Ros-diffs mailing list