- Sync with trunk r58248 to bring the latest changes from Amine (headers) and others...
[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 while (*p && *p <= _T(' '))
52 p++;
53 return p;
54 }
55
56 /* Used to check for and handle:
57 * SET "var=value", SET /P "var=prompt", and SET /P var="prompt" */
58 static LPTSTR
59 GetQuotedString(TCHAR *p)
60 {
61 TCHAR *end;
62 if (*p == _T('"'))
63 {
64 p = (LPTSTR)skip_ws(p + 1);
65 /* If a matching quote is found, truncate the string */
66 end = _tcsrchr(p, _T('"'));
67 if (end)
68 *end = _T('\0');
69 }
70 return p;
71 }
72
73 INT cmd_set (LPTSTR param)
74 {
75 LPTSTR p;
76 LPTSTR lpEnv;
77 LPTSTR lpOutput;
78
79 if ( !_tcsncmp (param, _T("/?"), 2) )
80 {
81 ConOutResPaging(TRUE,STRING_SET_HELP);
82 return 0;
83 }
84
85 param = (LPTSTR)skip_ws(param);
86
87 /* if no parameters, show the environment */
88 if (param[0] == _T('\0'))
89 {
90 lpEnv = (LPTSTR)GetEnvironmentStrings ();
91 if (lpEnv)
92 {
93 lpOutput = lpEnv;
94 while (*lpOutput)
95 {
96 if (*lpOutput != _T('='))
97 ConOutPuts(lpOutput);
98 lpOutput += _tcslen(lpOutput) + 1;
99 ConOutPuts(_T("\r\n"));
100 }
101 FreeEnvironmentStrings (lpEnv);
102 }
103
104 return 0;
105 }
106
107 /* the /A does *NOT* have to be followed by a whitespace */
108 if ( !_tcsnicmp (param, _T("/A"), 2) )
109 {
110 BOOL Success;
111 StripQuotes(param);
112 Success = seta_eval ( skip_ws(param+2) );
113 if(!Success)
114 {
115 /*might seem random but this is what windows xp does */
116 nErrorLevel = 9165;
117 }
118 return !Success;
119 }
120
121 if (!_tcsnicmp(param, _T("/P"), 2))
122 {
123 TCHAR value[1023];
124 param = GetQuotedString((LPTSTR)skip_ws(param + 2));
125 p = _tcschr(param, _T('='));
126 if (!p)
127 {
128 ConErrResPuts(STRING_SYNTAX_COMMAND_INCORRECT);
129 nErrorLevel = 1;
130 return 1;
131 }
132
133 *p++ = _T('\0');
134 ConOutPrintf(_T("%s"), GetQuotedString(p));
135 ConInString(value, 1023);
136
137 if (!*value || !SetEnvironmentVariable(param, value))
138 {
139 nErrorLevel = 1;
140 return 1;
141 }
142 return 0;
143 }
144
145 param = GetQuotedString(param);
146
147 p = _tcschr (param, _T('='));
148 if (p)
149 {
150 /* set or remove environment variable */
151 if (p == param)
152 {
153 /* handle set =val case */
154 ConErrResPuts(STRING_SYNTAX_COMMAND_INCORRECT);
155 nErrorLevel = 1;
156 return 1;
157 }
158
159 *p++ = _T('\0');
160 if (!SetEnvironmentVariable(param, *p ? p : NULL))
161 {
162 nErrorLevel = 1;
163 return 1;
164 }
165 }
166 else
167 {
168 /* display all environment variable with the given prefix */
169 BOOL bFound = FALSE;
170
171 while (_istspace(*param) || *param == _T(',') || *param == _T(';'))
172 param++;
173
174 p = _tcsrchr(param, _T(' '));
175 if (!p)
176 p = param + _tcslen(param);
177 *p = _T('\0');
178
179 lpEnv = GetEnvironmentStrings();
180 if (lpEnv)
181 {
182 lpOutput = lpEnv;
183 while (*lpOutput)
184 {
185 if (!_tcsnicmp(lpOutput, param, p - param))
186 {
187 ConOutPuts(lpOutput);
188 ConOutPuts(_T("\r\n"));
189 bFound = TRUE;
190 }
191 lpOutput += _tcslen(lpOutput) + 1;
192 }
193 FreeEnvironmentStrings(lpEnv);
194 }
195
196 if (!bFound)
197 {
198 ConErrResPrintf (STRING_PATH_ERROR, param);
199 nErrorLevel = 1;
200 return 1;
201 }
202 }
203
204 return 0;
205 }
206
207 static INT
208 ident_len ( LPCTSTR p )
209 {
210 LPCTSTR p2 = p;
211 if ( __iscsymf(*p) )
212 {
213 ++p2;
214 while ( __iscsym(*p2) )
215 ++p2;
216 }
217 return (INT)(p2-p);
218 }
219
220 #define PARSE_IDENT(ident,identlen,p) \
221 identlen = ident_len(p); \
222 ident = (LPTSTR)alloca ( ( identlen + 1 ) * sizeof(TCHAR) ); \
223 memmove ( ident, p, identlen * sizeof(TCHAR) ); \
224 ident[identlen] = 0; \
225 p += identlen;
226
227 static BOOL
228 seta_identval ( LPCTSTR ident, INT* result )
229 {
230 LPCTSTR identVal = GetEnvVarOrSpecial ( ident );
231 if ( !identVal )
232 {
233 /* TODO FIXME - what to do upon failure? */
234 *result = 0;
235 return FALSE;
236 }
237 *result = _tcstol ( identVal, NULL, 0 );
238 return TRUE;
239 }
240
241 static BOOL
242 calc ( INT* lval, TCHAR op, INT rval )
243 {
244 switch ( op )
245 {
246 case '*':
247 *lval *= rval;
248 break;
249 case '/':
250 *lval /= rval;
251 break;
252 case '%':
253 *lval %= rval;
254 break;
255 case '+':
256 *lval += rval;
257 break;
258 case '-':
259 *lval -= rval;
260 break;
261 case '&':
262 *lval &= rval;
263 break;
264 case '^':
265 *lval ^= rval;
266 break;
267 case '|':
268 *lval |= rval;
269 break;
270 default:
271 ConErrResPuts ( STRING_INVALID_OPERAND );
272 return FALSE;
273 }
274 return TRUE;
275 }
276
277 static BOOL
278 seta_stmt ( LPCTSTR* p_, INT* result );
279
280 static BOOL
281 seta_unaryTerm ( LPCTSTR* p_, INT* result )
282 {
283 LPCTSTR p = *p_;
284 if ( *p == _T('(') )
285 {
286 INT rval;
287 p = skip_ws ( p + 1 );
288 if ( !seta_stmt ( &p, &rval ) )
289 return FALSE;
290 if ( *p++ != _T(')') )
291 {
292 ConErrResPuts ( STRING_EXPECTED_CLOSE_PAREN );
293 return FALSE;
294 }
295 *result = rval;
296 }
297 else if ( isdigit(*p) )
298 {
299 *result = _tcstol ( p, (LPTSTR *)&p, 0 );
300 }
301 else if ( __iscsymf(*p) )
302 {
303 LPTSTR ident;
304 INT identlen;
305 PARSE_IDENT(ident,identlen,p);
306 if ( !seta_identval ( ident, result ) )
307 return FALSE;
308 }
309 else
310 {
311 ConErrResPuts ( STRING_EXPECTED_NUMBER_OR_VARIABLE );
312 return FALSE;
313 }
314 *p_ = skip_ws ( p );
315 return TRUE;
316 }
317
318 static BOOL
319 seta_mulTerm ( LPCTSTR* p_, INT* result )
320 {
321 LPCTSTR p = *p_;
322 TCHAR op = 0;
323 INT rval;
324 if ( _tcschr(_T("!~-"),*p) )
325 {
326 op = *p;
327 p = skip_ws ( p + 1 );
328 }
329 if ( !seta_unaryTerm ( &p, &rval ) )
330 return FALSE;
331 switch ( op )
332 {
333 case '!':
334 rval = !rval;
335 break;
336 case '~':
337 rval = ~rval;
338 break;
339 case '-':
340 rval = -rval;
341 break;
342 }
343
344 *result = rval;
345 *p_ = p;
346 return TRUE;
347 }
348
349 static BOOL
350 seta_ltorTerm ( LPCTSTR* p_, INT* result, LPCTSTR ops, BOOL (*subTerm)(LPCTSTR*,INT*) )
351 {
352 LPCTSTR p = *p_;
353 INT lval;
354 if ( !subTerm ( &p, &lval ) )
355 return FALSE;
356 while ( *p && _tcschr(ops,*p) )
357 {
358 INT rval;
359 TCHAR op = *p;
360
361 p = skip_ws ( p+1 );
362
363 if ( !subTerm ( &p, &rval ) )
364 return FALSE;
365
366 if ( !calc ( &lval, op, rval ) )
367 return FALSE;
368 }
369
370 *result = lval;
371 *p_ = p;
372 return TRUE;
373 }
374
375 static BOOL
376 seta_addTerm ( LPCTSTR* p_, INT* result )
377 {
378 return seta_ltorTerm ( p_, result, _T("*/%"), seta_mulTerm );
379 }
380
381 static BOOL
382 seta_logShiftTerm ( LPCTSTR* p_, INT* result )
383 {
384 return seta_ltorTerm ( p_, result, _T("+-"), seta_addTerm );
385 }
386
387 static BOOL
388 seta_bitAndTerm ( LPCTSTR* p_, INT* result )
389 {
390 LPCTSTR p = *p_;
391 INT lval;
392 if ( !seta_logShiftTerm ( &p, &lval ) )
393 return FALSE;
394 while ( *p && _tcschr(_T("<>"),*p) && p[0] == p[1] )
395 {
396 INT rval;
397 TCHAR op = *p;
398
399 p = skip_ws ( p+2 );
400
401 if ( !seta_logShiftTerm ( &p, &rval ) )
402 return FALSE;
403
404 switch ( op )
405 {
406 case '<':
407 lval <<= rval;
408 break;
409 case '>':
410 lval >>= rval;
411 break;
412 default:
413 ConErrResPuts ( STRING_INVALID_OPERAND );
414 return FALSE;
415 }
416 }
417
418 *result = lval;
419 *p_ = p;
420 return TRUE;
421 }
422
423 static BOOL
424 seta_bitExclOrTerm ( LPCTSTR* p_, INT* result )
425 {
426 return seta_ltorTerm ( p_, result, _T("&"), seta_bitAndTerm );
427 }
428
429 static BOOL
430 seta_bitOrTerm ( LPCTSTR* p_, INT* result )
431 {
432 return seta_ltorTerm ( p_, result, _T("^"), seta_bitExclOrTerm );
433 }
434
435 static BOOL
436 seta_expr ( LPCTSTR* p_, INT* result )
437 {
438 return seta_ltorTerm ( p_, result, _T("|"), seta_bitOrTerm );
439 }
440
441 static BOOL
442 seta_assignment ( LPCTSTR* p_, INT* result )
443 {
444 LPCTSTR p = *p_;
445 LPTSTR ident;
446 TCHAR op = 0;
447 INT identlen, exprval;
448
449 PARSE_IDENT(ident,identlen,p);
450 if ( identlen )
451 {
452 p = skip_ws(p);
453 if ( *p == _T('=') )
454 op = *p, p = skip_ws(p+1);
455 else if ( _tcschr ( _T("*/%+-&^|"), *p ) && p[1] == _T('=') )
456 op = *p, p = skip_ws(p+2);
457 else if ( _tcschr ( _T("<>"), *p ) && *p == p[1] && p[2] == _T('=') )
458 op = *p, p = skip_ws(p+3);
459 }
460
461 /* allow to chain multiple assignments, such as: a=b=1 */
462 if ( ident && op )
463 {
464 INT identval;
465 LPTSTR buf;
466
467 if ( !seta_assignment ( &p, &exprval ) )
468 return FALSE;
469
470 if ( !seta_identval ( ident, &identval ) )
471 identval = 0;
472 switch ( op )
473 {
474 case '=':
475 identval = exprval;
476 break;
477 case '<':
478 identval <<= exprval;
479 break;
480 case '>':
481 identval >>= exprval;
482 break;
483 default:
484 if ( !calc ( &identval, op, exprval ) )
485 return FALSE;
486 }
487 buf = (LPTSTR)alloca ( 32 * sizeof(TCHAR) );
488 _sntprintf ( buf, 32, _T("%i"), identval );
489 SetEnvironmentVariable ( ident, buf ); // TODO FIXME - check return value
490 exprval = identval;
491 }
492 else
493 {
494 /* restore p in case we found an ident but not an op */
495 p = *p_;
496 if ( !seta_expr ( &p, &exprval ) )
497 return FALSE;
498 }
499
500 *result = exprval;
501 *p_ = p;
502 return TRUE;
503 }
504
505 static BOOL
506 seta_stmt ( LPCTSTR* p_, INT* result )
507 {
508 LPCTSTR p = *p_;
509 INT rval;
510
511 if ( !seta_assignment ( &p, &rval ) )
512 return FALSE;
513 while ( *p == _T(',') )
514 {
515 p = skip_ws ( p+1 );
516
517 if ( !seta_assignment ( &p, &rval ) )
518 return FALSE;
519 }
520
521 *result = rval;
522 *p_ = p;
523 return TRUE;
524 }
525
526 static BOOL
527 seta_eval ( LPCTSTR p )
528 {
529 INT rval;
530 if ( !*p )
531 {
532 ConErrResPuts ( STRING_SYNTAX_COMMAND_INCORRECT );
533 return FALSE;
534 }
535 if ( !seta_stmt ( &p, &rval ) )
536 return FALSE;
537 if ( !bc )
538 ConOutPrintf ( _T("%i"), rval );
539 return TRUE;
540 }
541
542 #endif