move hard-coded strings to En.rc before GreatLord shoots me
[reactos.git] / reactos / subsys / system / 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 malloc'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 <ekohl@abo.rhein-zeitung.de>)
24 * Added help text ("/?").
25 *
26 * 24-Jan-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
27 * Fixed Win32 environment handling.
28 * Unicode and redirection safe!
29 *
30 * 25-Feb-1999 (Eric Kohl <ekohl@abo.rhein-zeitung.de>)
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 #include <malloc.h>
39 #include <stdio.h>
40 #include "resource.h"
41
42 #ifdef INCLUDE_CMD_SET
43
44
45 /* initial size of environment variable buffer */
46 #define ENV_BUFFER_SIZE 1024
47
48 static BOOL
49 seta_eval ( LPCTSTR expr );
50
51 static LPCTSTR
52 skip_ws ( LPCTSTR p )
53 {
54 return p + _tcsspn ( p, _T(" \t") );
55 }
56
57 INT cmd_set (LPTSTR cmd, LPTSTR param)
58 {
59 TCHAR szMsg[RC_STRING_MAX_SIZE];
60 INT i;
61 LPTSTR p;
62
63 if ( !_tcsncmp (param, _T("/?"), 2) )
64 {
65 ConOutResPaging(TRUE,STRING_SET_HELP);
66 return 0;
67 }
68
69 /* remove escapes */
70 if ( param[0] ) for ( i = 0; param[i+1]; i++ )
71 {
72 if ( param[i] == '^' && strchr("<|>&^",param[i+1]) )
73 {
74 memmove ( &param[i], &param[i+1], _tcslen(&param[i]) * sizeof(TCHAR) );
75 ++i;
76 }
77 }
78
79 /* if no parameters, show the environment */
80 if (param[0] == _T('\0'))
81 {
82 LPTSTR lpEnv;
83 LPTSTR lpOutput;
84 INT len;
85
86 lpEnv = (LPTSTR)GetEnvironmentStrings ();
87 if (lpEnv)
88 {
89 lpOutput = lpEnv;
90 while (*lpOutput)
91 {
92 len = _tcslen(lpOutput);
93 if (len)
94 {
95 if (*lpOutput != _T('='))
96 ConOutPuts (lpOutput);
97 lpOutput += (len + 1);
98 }
99 }
100 FreeEnvironmentStrings (lpEnv);
101 }
102
103 return 0;
104 }
105
106 if ( !_tcsnicmp (param, _T("/A"), 2) )
107 {
108 // TODO FIXME - what are we supposed to return?
109 return seta_eval ( skip_ws(param+2) );
110 }
111
112 p = _tcschr (param, _T('='));
113 if (p)
114 {
115 /* set or remove environment variable */
116 *p = _T('\0');
117 p++;
118 if (*p == _T('\0'))
119 {
120 p = NULL;
121 }
122 SetEnvironmentVariable (param, p);
123 }
124 else
125 {
126 /* display environment variable */
127 LPTSTR pszBuffer;
128 DWORD dwBuffer;
129
130 pszBuffer = (LPTSTR)malloc (ENV_BUFFER_SIZE * sizeof(TCHAR));
131 dwBuffer = GetEnvironmentVariable (param, pszBuffer, ENV_BUFFER_SIZE);
132 if (dwBuffer == 0)
133 {
134 LoadString(CMD_ModuleHandle, STRING_PATH_ERROR, szMsg, RC_STRING_MAX_SIZE);
135 ConErrPrintf (szMsg, param);
136 return 0;
137 }
138 else if (dwBuffer > ENV_BUFFER_SIZE)
139 {
140 pszBuffer = (LPTSTR)realloc (pszBuffer, dwBuffer * sizeof (TCHAR));
141 GetEnvironmentVariable (param, pszBuffer, dwBuffer);
142 }
143 ConOutPrintf (_T("%s\n"), pszBuffer);
144
145 free (pszBuffer);
146
147 return 0;
148 }
149
150 return 0;
151 }
152
153 static INT
154 ident_len ( LPCTSTR p )
155 {
156 LPCTSTR p2 = p;
157 if ( __iscsymf(*p) )
158 {
159 ++p2;
160 while ( __iscsym(*p2) )
161 ++p2;
162 }
163 return p2-p;
164 }
165
166 #define PARSE_IDENT(ident,identlen,p) \
167 identlen = ident_len(p); \
168 ident = (LPTSTR)alloca ( ( identlen + 1 ) * sizeof(TCHAR) ); \
169 memmove ( ident, p, identlen * sizeof(TCHAR) ); \
170 ident[identlen] = 0; \
171 p += identlen;
172
173 static BOOL
174 seta_identval ( LPCTSTR ident, INT* result )
175 {
176 // get size of buffer for env var
177 LPTSTR buf;
178 DWORD dwBuffer = GetEnvironmentVariable ( ident, NULL, 0 );
179 // if GetEnvironmentVariable() fails, it returns 0
180 if ( !dwBuffer )
181 {
182 // TODO FIXME - is it correct to report a value of 0 for non-existant variables?
183 *result = 0;
184 return FALSE;
185 }
186 buf = (LPTSTR)alloca ( dwBuffer * sizeof(TCHAR) );
187 if ( !buf )
188 {
189 // TODO FIXME - is it correct to report 0 when report resources low... should we error somehow?
190 *result = 0;
191 return FALSE;
192 }
193 GetEnvironmentVariable ( ident, buf, dwBuffer );
194 *result = _ttoi ( buf );
195 return TRUE;
196 }
197
198 static BOOL
199 calc ( INT* lval, TCHAR op, INT rval )
200 {
201 switch ( op )
202 {
203 case '*':
204 *lval *= rval;
205 break;
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 default:
228 ConErrResPuts ( STRING_INVALID_OPERAND );
229 return FALSE;
230 }
231 return TRUE;
232 }
233
234 static BOOL
235 seta_stmt ( LPCTSTR* p_, INT* result );
236
237 static BOOL
238 seta_unaryTerm ( LPCTSTR* p_, INT* result )
239 {
240 LPCTSTR p = *p_;
241 if ( *p == _T('(') )
242 {
243 INT rval;
244 p = skip_ws ( p + 1 );
245 if ( !seta_stmt ( &p, &rval ) )
246 return FALSE;
247 if ( *p != _T(')') )
248 {
249 ConErrResPuts ( STRING_EXPECTED_CLOSE_PAREN );
250 return FALSE;
251 }
252 *result = rval;
253 p = skip_ws ( p + 1 );
254 }
255 else if ( isdigit(*p) )
256 {
257 *result = _ttoi ( p );
258 p = skip_ws ( p + _tcsspn ( p, _T("1234567890") ) );
259 }
260 else if ( __iscsymf(*p) )
261 {
262 LPTSTR ident;
263 INT identlen;
264 PARSE_IDENT(ident,identlen,p);
265 if ( !seta_identval ( ident, result ) )
266 return FALSE;
267 }
268 else
269 {
270 ConErrResPuts ( STRING_EXPECTED_NUMBER_OR_VARIABLE );
271 return FALSE;
272 }
273 *p_ = p;
274 return TRUE;
275 }
276
277 static BOOL
278 seta_mulTerm ( LPCTSTR* p_, INT* result )
279 {
280 LPCTSTR p = *p_;
281 TCHAR op = 0;
282 INT rval;
283 if ( _tcschr(_T("!~-"),*p) )
284 {
285 op = *p;
286 p = skip_ws ( p + 1 );
287 }
288 if ( !seta_unaryTerm ( &p, &rval ) )
289 return FALSE;
290 switch ( op )
291 {
292 case '!':
293 rval = !rval;
294 break;
295 case '~':
296 rval = ~rval;
297 break;
298 case '-':
299 rval = -rval;
300 break;
301 }
302
303 *result = rval;
304 *p_ = p;
305 return TRUE;
306 }
307
308 static BOOL
309 seta_ltorTerm ( LPCTSTR* p_, INT* result, LPCTSTR ops, BOOL (*subTerm)(LPCTSTR*,INT*) )
310 {
311 LPCTSTR p = *p_;
312 INT lval;
313 if ( !subTerm ( &p, &lval ) )
314 return FALSE;
315 while ( *p && _tcschr(ops,*p) )
316 {
317 INT rval;
318 TCHAR op = *p;
319
320 p = skip_ws ( p+1 );
321
322 if ( !subTerm ( &p, &rval ) )
323 return FALSE;
324
325 if ( !calc ( &lval, op, rval ) )
326 return FALSE;
327 }
328
329 *result = lval;
330 *p_ = p;
331 return TRUE;
332 }
333
334 static BOOL
335 seta_addTerm ( LPCTSTR* p_, INT* result )
336 {
337 return seta_ltorTerm ( p_, result, _T("*/%"), seta_mulTerm );
338 }
339
340 static BOOL
341 seta_logShiftTerm ( LPCTSTR* p_, INT* result )
342 {
343 return seta_ltorTerm ( p_, result, _T("+-"), seta_addTerm );
344 }
345
346 static BOOL
347 seta_bitAndTerm ( LPCTSTR* p_, INT* result )
348 {
349 LPCTSTR p = *p_;
350 INT lval;
351 if ( !seta_logShiftTerm ( &p, &lval ) )
352 return FALSE;
353 while ( *p && _tcschr(_T("<>"),*p) && p[0] == p[1] )
354 {
355 INT rval;
356 TCHAR op = *p;
357
358 p = skip_ws ( p+2 );
359
360 if ( !seta_logShiftTerm ( &p, &rval ) )
361 return FALSE;
362
363 switch ( op )
364 {
365 case '<':
366 lval <<= rval;
367 break;
368 case '>':
369 lval >>= rval;
370 break;
371 default:
372 ConErrResPuts ( STRING_INVALID_OPERAND );
373 return FALSE;
374 }
375 }
376
377 *result = lval;
378 *p_ = p;
379 return TRUE;
380 }
381
382 static BOOL
383 seta_bitExclOrTerm ( LPCTSTR* p_, INT* result )
384 {
385 return seta_ltorTerm ( p_, result, _T("&"), seta_bitAndTerm );
386 }
387
388 static BOOL
389 seta_bitOrTerm ( LPCTSTR* p_, INT* result )
390 {
391 return seta_ltorTerm ( p_, result, _T("^"), seta_bitExclOrTerm );
392 }
393
394 static BOOL
395 seta_expr ( LPCTSTR* p_, INT* result )
396 {
397 return seta_ltorTerm ( p_, result, _T("|"), seta_bitOrTerm );
398 }
399
400 static BOOL
401 seta_assignment ( LPCTSTR* p_, INT* result )
402 {
403 LPCTSTR p = *p_;
404 LPTSTR ident;
405 TCHAR op = 0;
406 INT identlen, exprval;
407
408 PARSE_IDENT(ident,identlen,p);
409 if ( identlen )
410 {
411 if ( *p == _T('=') )
412 op = *p, p = skip_ws(p+1);
413 else if ( _tcschr ( _T("*/%+-&^|"), *p ) && p[1] == _T('=') )
414 op = *p, p = skip_ws(p+2);
415 else if ( _tcschr ( _T("<>"), *p ) && *p == p[1] && p[2] == _T('=') )
416 op = *p, p = skip_ws(p+3);
417 }
418
419 /* allow to chain multiple assignments, such as: a=b=1 */
420 if ( ident && op )
421 {
422 INT identval;
423 LPTSTR buf;
424
425 if ( !seta_assignment ( &p, &exprval ) )
426 return FALSE;
427
428 if ( !seta_identval ( ident, &identval ) )
429 identval = 0;
430 switch ( op )
431 {
432 case '=':
433 identval = exprval;
434 break;
435 case '<':
436 identval <<= exprval;
437 break;
438 case '>':
439 identval >>= exprval;
440 break;
441 default:
442 if ( !calc ( &identval, op, exprval ) )
443 return FALSE;
444 }
445 buf = (LPTSTR)alloca ( 32 * sizeof(TCHAR) );
446 _sntprintf ( buf, 32, _T("%i"), identval );
447 SetEnvironmentVariable ( ident, buf ); // TODO FIXME - check return value
448 exprval = identval;
449 }
450 else
451 {
452 /* restore p in case we found an ident but not an op */
453 p = *p_;
454 if ( !seta_expr ( &p, &exprval ) )
455 return FALSE;
456 }
457
458 *result = exprval;
459 *p_ = p;
460 return TRUE;
461 }
462
463 static BOOL
464 seta_stmt ( LPCTSTR* p_, INT* result )
465 {
466 LPCTSTR p = *p_;
467 INT rval;
468
469 if ( !seta_assignment ( &p, &rval ) )
470 return FALSE;
471 while ( *p == _T(',') )
472 {
473 p = skip_ws ( p+1 );
474
475 if ( !seta_assignment ( &p, &rval ) )
476 return FALSE;
477 }
478
479 *result = rval;
480 *p_ = p;
481 return TRUE;
482 }
483
484 static BOOL
485 seta_eval ( LPCTSTR p )
486 {
487 INT rval;
488 if ( !*p )
489 {
490 ConErrResPuts ( STRING_SYNTAX_COMMAND_INCORRECT );
491 return FALSE;
492 }
493 if ( !seta_stmt ( &p, &rval ) )
494 return FALSE;
495 ConOutPrintf ( _T("%i\n"), rval );
496 return TRUE;
497 }
498
499 #endif