- Implement ProtocolResetComplete
[reactos.git] / base / shell / cmd / set.c
1 /*
2 * SET.C - set internal command.
3 *
4 *
5 * History:
6 *
7 * 06/14/97 (Tim Norman)
8 * changed static var in set() to a cmd_alloc'd space to pass to putenv.
9 * need to find a better way to do this, since it seems it is wasting
10 * memory when variables are redefined.
11 *
12 * 07/08/1998 (John P. Price)
13 * removed call to show_environment in set command.
14 * moved test for syntax before allocating memory in set command.
15 * misc clean up and optimization.
16 *
17 * 27-Jul-1998 (John P Price <linux-guru@gcfl.net>)
18 * added config.h include
19 *
20 * 28-Jul-1998 (John P Price <linux-guru@gcfl.net>)
21 * added set_env function to set env. variable without needing set command
22 *
23 * 09-Dec-1998 (Eric Kohl)
24 * Added help text ("/?").
25 *
26 * 24-Jan-1999 (Eric Kohl)
27 * Fixed Win32 environment handling.
28 * Unicode and redirection safe!
29 *
30 * 25-Feb-1999 (Eric Kohl)
31 * Fixed little bug.
32 *
33 * 30-Apr-2005 (Magnus Olsen) <magnus@greatlord.com>)
34 * Remove all hardcode string to En.rc
35 */
36
37 #include <precomp.h>
38
39 #ifdef INCLUDE_CMD_SET
40
41
42 /* initial size of environment variable buffer */
43 #define ENV_BUFFER_SIZE 1024
44
45 static BOOL
46 seta_eval ( LPCTSTR expr );
47
48 static LPCTSTR
49 skip_ws ( LPCTSTR p )
50 {
51 return p + _tcsspn ( p, _T(" \t") );
52 }
53
54 INT cmd_set (LPTSTR cmd, LPTSTR param)
55 {
56 INT i;
57 LPTSTR p;
58
59 if ( !_tcsncmp (param, _T("/?"), 2) )
60 {
61 ConOutResPaging(TRUE,STRING_SET_HELP);
62 return 0;
63 }
64
65 /* remove escapes */
66 if ( param[0] ) for ( i = 0; param[i+1]; i++ )
67 {
68 if ( param[i] == _T('^') )
69 {
70 memmove ( &param[i], &param[i+1], _tcslen(&param[i]) * sizeof(TCHAR) );
71 }
72 }
73
74 /* if no parameters, show the environment */
75 if (param[0] == _T('\0'))
76 {
77 LPTSTR lpEnv;
78 LPTSTR lpOutput;
79 INT len;
80
81 lpEnv = (LPTSTR)GetEnvironmentStrings ();
82 if (lpEnv)
83 {
84 lpOutput = lpEnv;
85 while (*lpOutput)
86 {
87 len = _tcslen(lpOutput);
88 if (len)
89 {
90 if (*lpOutput != _T('='))
91 ConOutPuts (lpOutput);
92 lpOutput += (len + 1);
93 }
94 }
95 FreeEnvironmentStrings (lpEnv);
96 }
97
98 return 0;
99 }
100
101 /* the /A does *NOT* have to be followed by a whitespace */
102 if ( !_tcsnicmp (param, _T("/A"), 2) )
103 {
104 BOOL Success = seta_eval ( skip_ws(param+2) );
105 if(!Success)
106 {
107 /*might seem random but this is what windows xp does */
108 nErrorLevel = 9165;
109 }
110 /* TODO FIXME - what are we supposed to return? */
111 return Success;
112 }
113
114 if ( !_tcsnicmp (param, _T("/"), 1) )
115 {
116 ConErrResPrintf (STRING_SYNTAX_COMMAND_INCORRECT, param);
117 return 0;
118 }
119
120 p = _tcschr (param, _T('='));
121 if (p)
122 {
123 /* set or remove environment variable */
124 if (p == param)
125 {
126 /* handle set =val case */
127 ConErrResPrintf (STRING_SYNTAX_COMMAND_INCORRECT, param);
128 return 0;
129 }
130
131 *p = _T('\0');
132 p++;
133 if (*p == _T('\0'))
134 {
135 p = NULL;
136 }
137 SetEnvironmentVariable (param, p);
138 }
139 else
140 {
141 /* display environment variable */
142 LPTSTR pszBuffer;
143 DWORD dwBuffer;
144
145 pszBuffer = (LPTSTR)cmd_alloc (ENV_BUFFER_SIZE * sizeof(TCHAR));
146 dwBuffer = GetEnvironmentVariable (param, pszBuffer, ENV_BUFFER_SIZE);
147 if (dwBuffer == 0)
148 {
149 ConErrResPrintf (STRING_PATH_ERROR, param);
150 return 0;
151 }
152 else if (dwBuffer > ENV_BUFFER_SIZE)
153 {
154 pszBuffer = (LPTSTR)cmd_realloc (pszBuffer, dwBuffer * sizeof (TCHAR));
155 GetEnvironmentVariable (param, pszBuffer, dwBuffer);
156 }
157 ConOutPrintf (_T("%s\n"), pszBuffer);
158
159 cmd_free (pszBuffer);
160
161 return 0;
162 }
163
164 return 0;
165 }
166
167 static INT
168 ident_len ( LPCTSTR p )
169 {
170 LPCTSTR p2 = p;
171 if ( __iscsymf(*p) )
172 {
173 ++p2;
174 while ( __iscsym(*p2) )
175 ++p2;
176 }
177 return p2-p;
178 }
179
180 #define PARSE_IDENT(ident,identlen,p) \
181 identlen = ident_len(p); \
182 ident = (LPTSTR)alloca ( ( identlen + 1 ) * sizeof(TCHAR) ); \
183 memmove ( ident, p, identlen * sizeof(TCHAR) ); \
184 ident[identlen] = 0; \
185 p += identlen;
186
187 static BOOL
188 seta_identval ( LPCTSTR ident, INT* result )
189 {
190 LPCTSTR identVal = GetEnvVarOrSpecial ( ident );
191 if ( !identVal )
192 {
193 /* TODO FIXME - what to do upon failure? */
194 *result = 0;
195 return FALSE;
196 }
197 *result = _ttoi ( identVal );
198 return TRUE;
199 }
200
201 static BOOL
202 calc ( INT* lval, TCHAR op, INT rval )
203 {
204 switch ( op )
205 {
206 case '*':
207 *lval *= rval;
208 break;
209 case '/':
210 *lval /= rval;
211 break;
212 case '%':
213 *lval %= rval;
214 break;
215 case '+':
216 *lval += rval;
217 break;
218 case '-':
219 *lval -= rval;
220 break;
221 case '&':
222 *lval &= rval;
223 break;
224 case '^':
225 *lval ^= rval;
226 break;
227 case '|':
228 *lval |= rval;
229 break;
230 default:
231 ConErrResPuts ( STRING_INVALID_OPERAND );
232 return FALSE;
233 }
234 return TRUE;
235 }
236
237 static BOOL
238 seta_stmt ( LPCTSTR* p_, INT* result );
239
240 static BOOL
241 seta_unaryTerm ( LPCTSTR* p_, INT* result )
242 {
243 LPCTSTR p = *p_;
244 if ( *p == _T('(') )
245 {
246 INT rval;
247 p = skip_ws ( p + 1 );
248 if ( !seta_stmt ( &p, &rval ) )
249 return FALSE;
250 if ( *p != _T(')') )
251 {
252 ConErrResPuts ( STRING_EXPECTED_CLOSE_PAREN );
253 return FALSE;
254 }
255 *result = rval;
256 p = skip_ws ( p + 1 );
257 }
258 else if ( isdigit(*p) )
259 {
260 *result = _ttoi ( p );
261 p = skip_ws ( p + _tcsspn ( p, _T("1234567890") ) );
262 }
263 else if ( __iscsymf(*p) )
264 {
265 LPTSTR ident;
266 INT identlen;
267 PARSE_IDENT(ident,identlen,p);
268 if ( !seta_identval ( ident, result ) )
269 return FALSE;
270 }
271 else
272 {
273 ConErrResPuts ( STRING_EXPECTED_NUMBER_OR_VARIABLE );
274 return FALSE;
275 }
276 *p_ = p;
277 return TRUE;
278 }
279
280 static BOOL
281 seta_mulTerm ( LPCTSTR* p_, INT* result )
282 {
283 LPCTSTR p = *p_;
284 TCHAR op = 0;
285 INT rval;
286 if ( _tcschr(_T("!~-"),*p) )
287 {
288 op = *p;
289 p = skip_ws ( p + 1 );
290 }
291 if ( !seta_unaryTerm ( &p, &rval ) )
292 return FALSE;
293 switch ( op )
294 {
295 case '!':
296 rval = !rval;
297 break;
298 case '~':
299 rval = ~rval;
300 break;
301 case '-':
302 rval = -rval;
303 break;
304 }
305
306 *result = rval;
307 *p_ = p;
308 return TRUE;
309 }
310
311 static BOOL
312 seta_ltorTerm ( LPCTSTR* p_, INT* result, LPCTSTR ops, BOOL (*subTerm)(LPCTSTR*,INT*) )
313 {
314 LPCTSTR p = *p_;
315 INT lval;
316 if ( !subTerm ( &p, &lval ) )
317 return FALSE;
318 while ( *p && _tcschr(ops,*p) )
319 {
320 INT rval;
321 TCHAR op = *p;
322
323 p = skip_ws ( p+1 );
324
325 if ( !subTerm ( &p, &rval ) )
326 return FALSE;
327
328 if ( !calc ( &lval, op, rval ) )
329 return FALSE;
330 }
331
332 *result = lval;
333 *p_ = p;
334 return TRUE;
335 }
336
337 static BOOL
338 seta_addTerm ( LPCTSTR* p_, INT* result )
339 {
340 return seta_ltorTerm ( p_, result, _T("*/%"), seta_mulTerm );
341 }
342
343 static BOOL
344 seta_logShiftTerm ( LPCTSTR* p_, INT* result )
345 {
346 return seta_ltorTerm ( p_, result, _T("+-"), seta_addTerm );
347 }
348
349 static BOOL
350 seta_bitAndTerm ( LPCTSTR* p_, INT* result )
351 {
352 LPCTSTR p = *p_;
353 INT lval;
354 if ( !seta_logShiftTerm ( &p, &lval ) )
355 return FALSE;
356 while ( *p && _tcschr(_T("<>"),*p) && p[0] == p[1] )
357 {
358 INT rval;
359 TCHAR op = *p;
360
361 p = skip_ws ( p+2 );
362
363 if ( !seta_logShiftTerm ( &p, &rval ) )
364 return FALSE;
365
366 switch ( op )
367 {
368 case '<':
369 lval <<= rval;
370 break;
371 case '>':
372 lval >>= rval;
373 break;
374 default:
375 ConErrResPuts ( STRING_INVALID_OPERAND );
376 return FALSE;
377 }
378 }
379
380 *result = lval;
381 *p_ = p;
382 return TRUE;
383 }
384
385 static BOOL
386 seta_bitExclOrTerm ( LPCTSTR* p_, INT* result )
387 {
388 return seta_ltorTerm ( p_, result, _T("&"), seta_bitAndTerm );
389 }
390
391 static BOOL
392 seta_bitOrTerm ( LPCTSTR* p_, INT* result )
393 {
394 return seta_ltorTerm ( p_, result, _T("^"), seta_bitExclOrTerm );
395 }
396
397 static BOOL
398 seta_expr ( LPCTSTR* p_, INT* result )
399 {
400 return seta_ltorTerm ( p_, result, _T("|"), seta_bitOrTerm );
401 }
402
403 static BOOL
404 seta_assignment ( LPCTSTR* p_, INT* result )
405 {
406 LPCTSTR p = *p_;
407 LPTSTR ident;
408 TCHAR op = 0;
409 INT identlen, exprval;
410
411 PARSE_IDENT(ident,identlen,p);
412 if ( identlen )
413 {
414 if ( *p == _T('=') )
415 op = *p, p = skip_ws(p+1);
416 else if ( _tcschr ( _T("*/%+-&^|"), *p ) && p[1] == _T('=') )
417 op = *p, p = skip_ws(p+2);
418 else if ( _tcschr ( _T("<>"), *p ) && *p == p[1] && p[2] == _T('=') )
419 op = *p, p = skip_ws(p+3);
420 }
421
422 /* allow to chain multiple assignments, such as: a=b=1 */
423 if ( ident && op )
424 {
425 INT identval;
426 LPTSTR buf;
427
428 if ( !seta_assignment ( &p, &exprval ) )
429 return FALSE;
430
431 if ( !seta_identval ( ident, &identval ) )
432 identval = 0;
433 switch ( op )
434 {
435 case '=':
436 identval = exprval;
437 break;
438 case '<':
439 identval <<= exprval;
440 break;
441 case '>':
442 identval >>= exprval;
443 break;
444 default:
445 if ( !calc ( &identval, op, exprval ) )
446 return FALSE;
447 }
448 buf = (LPTSTR)alloca ( 32 * sizeof(TCHAR) );
449 _sntprintf ( buf, 32, _T("%i"), identval );
450 SetEnvironmentVariable ( ident, buf ); // TODO FIXME - check return value
451 exprval = identval;
452 }
453 else
454 {
455 /* restore p in case we found an ident but not an op */
456 p = *p_;
457 if ( !seta_expr ( &p, &exprval ) )
458 return FALSE;
459 }
460
461 *result = exprval;
462 *p_ = p;
463 return TRUE;
464 }
465
466 static BOOL
467 seta_stmt ( LPCTSTR* p_, INT* result )
468 {
469 LPCTSTR p = *p_;
470 INT rval;
471
472 if ( !seta_assignment ( &p, &rval ) )
473 return FALSE;
474 while ( *p == _T(',') )
475 {
476 p = skip_ws ( p+1 );
477
478 if ( !seta_assignment ( &p, &rval ) )
479 return FALSE;
480 }
481
482 *result = rval;
483 *p_ = p;
484 return TRUE;
485 }
486
487 static BOOL
488 seta_eval ( LPCTSTR p )
489 {
490 INT rval;
491 if ( !*p )
492 {
493 ConErrResPuts ( STRING_SYNTAX_COMMAND_INCORRECT );
494 return FALSE;
495 }
496 if ( !seta_stmt ( &p, &rval ) )
497 return FALSE;
498 ConOutPrintf ( _T("%i"), rval );
499 return TRUE;
500 }
501
502 #endif