xref: /reactos/base/shell/cmd/setlocal.c (revision 40462c92)
1 /*
2  *  SETLOCAL.C - setlocal and endlocal internal batch commands.
3  *
4  *  History:
5  *
6  *    1 Feb 2008 (Christoph von Wittich)
7  *        started.
8 */
9 
10 #include "precomp.h"
11 
12 #include <direct.h> // For _getdrive().
13 
14 typedef struct _SETLOCAL
15 {
16     struct _SETLOCAL *Prev;
17     LPTSTR Environment;
18     INT CurDrive;
19     BOOL EnableExtensions;
20     BOOL DelayedExpansion;
21 } SETLOCAL, *PSETLOCAL;
22 
23 /* Create a copy of the current environment */
24 LPTSTR
25 DuplicateEnvironment(VOID)
26 {
27     LPTSTR Environ = GetEnvironmentStrings();
28     LPTSTR End, EnvironCopy;
29 
30     if (!Environ) return NULL;
31 
32     for (End = Environ; *End; End += _tcslen(End) + 1) ;
33     EnvironCopy = cmd_alloc((End + 1 - Environ) * sizeof(TCHAR));
34 
35     if (EnvironCopy)
36         memcpy(EnvironCopy, Environ, (End + 1 - Environ) * sizeof(TCHAR));
37 
38     FreeEnvironmentStrings(Environ);
39     return EnvironCopy;
40 }
41 
42 INT cmd_setlocal(LPTSTR param)
43 {
44     PSETLOCAL Saved;
45     LPTSTR* arg;
46     INT argc, i;
47 
48     if (!_tcscmp(param, _T("/?")))
49     {
50         // FIXME
51         ConOutPuts(_T("SETLOCAL help not implemented yet!\n"));
52         return 0;
53     }
54 
55     /* SETLOCAL only works inside a batch context */
56     if (!bc)
57         return 0;
58 
59     Saved = cmd_alloc(sizeof(SETLOCAL));
60     if (!Saved)
61     {
62         WARN("Cannot allocate memory for Saved!\n");
63         error_out_of_memory();
64         return 1;
65     }
66 
67     Saved->Environment = DuplicateEnvironment();
68     if (!Saved->Environment)
69     {
70         error_out_of_memory();
71         cmd_free(Saved);
72         return 1;
73     }
74     /*
75      * Save the current drive; the duplicated environment
76      * contains the corresponding current directory.
77      */
78     Saved->CurDrive = _getdrive();
79 
80     Saved->EnableExtensions = bEnableExtensions;
81     Saved->DelayedExpansion = bDelayedExpansion;
82 
83     Saved->Prev = bc->setlocal;
84     bc->setlocal = Saved;
85 
86     nErrorLevel = 0;
87 
88     arg = splitspace(param, &argc);
89     for (i = 0; i < argc; i++)
90     {
91         if (!_tcsicmp(arg[i], _T("ENABLEEXTENSIONS")))
92             bEnableExtensions = TRUE;
93         else if (!_tcsicmp(arg[i], _T("DISABLEEXTENSIONS")))
94             bEnableExtensions = FALSE;
95         else if (!_tcsicmp(arg[i], _T("ENABLEDELAYEDEXPANSION")))
96             bDelayedExpansion = TRUE;
97         else if (!_tcsicmp(arg[i], _T("DISABLEDELAYEDEXPANSION")))
98             bDelayedExpansion = FALSE;
99         else
100         {
101             error_invalid_parameter_format(arg[i]);
102             break;
103         }
104     }
105     freep(arg);
106 
107     return nErrorLevel;
108 }
109 
110 INT cmd_endlocal(LPTSTR param)
111 {
112     LPTSTR Environ, Name, Value;
113     PSETLOCAL Saved;
114     TCHAR drvEnvVar[] = _T("=?:");
115     TCHAR szCurrent[MAX_PATH];
116 
117     if (!_tcscmp(param, _T("/?")))
118     {
119         // FIXME
120         ConOutPuts(_T("ENDLOCAL help not implemented yet!\n"));
121         return 0;
122     }
123 
124     /* Pop a SETLOCAL struct off of this batch context's stack */
125     if (!bc || !(Saved = bc->setlocal))
126         return 0;
127     bc->setlocal = Saved->Prev;
128 
129     bEnableExtensions = Saved->EnableExtensions;
130     bDelayedExpansion = Saved->DelayedExpansion;
131 
132     /* First, clear out the environment. Since making any changes to the
133      * environment invalidates pointers obtained from GetEnvironmentStrings(),
134      * we must make a copy of it and get the variable names from that. */
135     Environ = DuplicateEnvironment();
136     if (Environ)
137     {
138         for (Name = Environ; *Name; Name += _tcslen(Name) + 1)
139         {
140             if (!(Value = _tcschr(Name + 1, _T('='))))
141                 continue;
142             *Value++ = _T('\0');
143             SetEnvironmentVariable(Name, NULL);
144             Name = Value;
145         }
146         cmd_free(Environ);
147     }
148 
149     /* Now, restore variables from the copy saved by cmd_setlocal() */
150     for (Name = Saved->Environment; *Name; Name += _tcslen(Name) + 1)
151     {
152         if (!(Value = _tcschr(Name + 1, _T('='))))
153             continue;
154         *Value++ = _T('\0');
155         SetEnvironmentVariable(Name, Value);
156         Name = Value;
157     }
158 
159     /* Restore the current drive and its current directory from the environment */
160     drvEnvVar[1] = _T('A') + Saved->CurDrive - 1;
161     if (!GetEnvironmentVariable(drvEnvVar, szCurrent, ARRAYSIZE(szCurrent)))
162     {
163         _stprintf(szCurrent, _T("%C:\\"), _T('A') + Saved->CurDrive - 1);
164     }
165     _tchdir(szCurrent); // SetRootPath(NULL, szCurrent);
166 
167     cmd_free(Saved->Environment);
168     cmd_free(Saved);
169     return 0;
170 }
171