ReactOS  0.4.15-dev-1152-g6c94e4f
set.c
Go to the documentation of this file.
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 hardcoded strings in En.rc
35  */
36 
37 #include "precomp.h"
38 
39 #ifdef INCLUDE_CMD_SET
40 
41 /* Initial size of environment variable buffer */
42 #define ENV_BUFFER_SIZE 1024
43 
44 static BOOL
46 
47 static LPCTSTR
49 {
50  while (*p && *p <= _T(' '))
51  ++p;
52  return p;
53 }
54 
55 /* Used to check for and handle:
56  * SET "var=value", SET /P "var=prompt", and SET /P var="prompt" */
57 static LPTSTR
59 {
60  TCHAR *end;
61  if (*p == _T('"'))
62  {
63  p = (LPTSTR)skip_ws(p + 1);
64  /* If a matching quote is found, truncate the string */
65  end = _tcsrchr(p, _T('"'));
66  if (end)
67  *end = _T('\0');
68  }
69  return p;
70 }
71 
73 {
74  INT retval = 0;
75  LPTSTR p;
76  LPTSTR lpEnv;
77  LPTSTR lpOutput;
78 
79  if (!_tcsncmp(param, _T("/?"), 2))
80  {
82  return 0;
83  }
84 
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  /* Do not display the special '=X:' environment variables */
97  if (*lpOutput != _T('='))
98  {
99  ConOutPuts(lpOutput);
100  ConOutChar(_T('\n'));
101  }
102  lpOutput += _tcslen(lpOutput) + 1;
103  }
104  FreeEnvironmentStrings(lpEnv);
105  }
106 
107  retval = 0;
108  goto Quit;
109  }
110 
111  /* The /A does *NOT* have to be followed by a whitespace */
112  if (!_tcsnicmp(param, _T("/A"), 2))
113  {
114  BOOL Success;
115 
116  /* Save error level since seta_eval() modifies it, as
117  * we need to set it later according to specific rules. */
118  INT nOldErrorLevel = nErrorLevel;
119 
121  Success = seta_eval(skip_ws(param + 2));
122  if (!Success)
123  {
124 #if 0
125  /* Might seem random but this is what windows xp does -- This is a message ID */
126  retval = 9165;
127 #endif
128  retval = nErrorLevel;
129  nErrorLevel = nOldErrorLevel;
130  }
131  else
132  {
133  retval = 0;
134  }
135  goto Quit;
136  }
137 
138  if (!_tcsnicmp(param, _T("/P"), 2))
139  {
140  TCHAR value[1023];
142  p = _tcschr(param, _T('='));
143  if (!p)
144  {
146  retval = 1;
147  goto Quit;
148  }
149 
150  *p++ = _T('\0');
151  ConOutPrintf(_T("%s"), GetQuotedString(p));
153 
155  {
156  retval = 1;
157  goto Quit;
158  }
159  retval = 0;
160  goto Quit;
161  }
162 
164 
165  p = _tcschr(param, _T('='));
166  if (p)
167  {
168  /* Set or remove the environment variable */
169  if (p == param)
170  {
171  /* Handle set =val case */
173  retval = 1;
174  goto Quit;
175  }
176 
177  *p++ = _T('\0');
178  if (!SetEnvironmentVariable(param, *p ? p : NULL))
179  {
180  retval = 1;
181  goto Quit;
182  }
183  }
184  else
185  {
186  /* Display all the environment variables with the given prefix */
187  LPTSTR pOrgParam = param;
188  BOOLEAN bFound = FALSE;
189  BOOLEAN bRestoreSpace;
190 
191  /*
192  * Trim the prefix from "special" characters (only when displaying the
193  * environment variables), so that e.g. "SET ,; ,;FOO" will display all
194  * the variables starting by "FOO".
195  * The SET command allows as well to set an environment variable whose name
196  * actually contains these characters (e.g. "SET ,; ,;FOO=42"); however,
197  * by trimming the characters, doing "SET ,; ,;FOO" would not allow seeing
198  * such variables.
199  * Thus, we also save a pointer to the original variable name prefix, that
200  * we will look it up as well below.
201  */
202  while (_istspace(*param) || *param == _T(',') || *param == _T(';'))
203  ++param;
204 
205  /* Just remove the very last space, if present */
206  p = _tcsrchr(param, _T(' '));
207  bRestoreSpace = (p != NULL);
208  if (!p)
209  p = param + _tcslen(param);
210  *p = _T('\0');
211 
212  lpEnv = GetEnvironmentStrings();
213  if (lpEnv)
214  {
215  lpOutput = lpEnv;
216  while (*lpOutput)
217  {
218  /* Look up for both the original and truncated variable name prefix */
219  if (!_tcsnicmp(lpOutput, pOrgParam, p - pOrgParam) ||
220  !_tcsnicmp(lpOutput, param, p - param))
221  {
222  ConOutPuts(lpOutput);
223  ConOutChar(_T('\n'));
224  bFound = TRUE;
225  }
226  lpOutput += _tcslen(lpOutput) + 1;
227  }
228  FreeEnvironmentStrings(lpEnv);
229  }
230 
231  /* Restore the truncated space for correctly
232  * displaying the error message, if any. */
233  if (bRestoreSpace)
234  *p = _T(' ');
235 
236  if (!bFound)
237  {
239  retval = 1;
240  goto Quit;
241  }
242  }
243 
244 Quit:
245  if (BatType != CMD_TYPE)
246  {
247  if (retval != 0)
248  nErrorLevel = retval;
249  }
250  else
251  {
252  nErrorLevel = retval;
253  }
254 
255  return retval;
256 }
257 
258 static INT
260 {
261  LPCTSTR p2 = p;
262  if (__iscsymf(*p))
263  {
264  ++p2;
265  while (__iscsym(*p2))
266  ++p2;
267  }
268  return (INT)(p2-p);
269 }
270 
271 #define PARSE_IDENT(ident, identlen, p) \
272 do { \
273  identlen = ident_len(p); \
274  ident = (LPTSTR)alloca((identlen + 1) * sizeof(TCHAR)); \
275  memmove(ident, p, identlen * sizeof(TCHAR)); \
276  ident[identlen] = 0; \
277  p += identlen; \
278 } while (0)
279 
280 static INT
282 {
283  LPCTSTR identVal = GetEnvVarOrSpecial(ident);
284  if (!identVal)
285  return 0;
286  else
287  return _tcstol(identVal, NULL, 0);
288 }
289 
290 static BOOL
292 {
293  switch (op)
294  {
295  case '*':
296  *lval *= rval;
297  break;
298 
299  case '/':
300  {
301  if (rval == 0)
302  {
303  // FIXME: Localize
304  ConErrPuts(_T("Division by zero error.\n"));
305  nErrorLevel = 0x400023D1; // 1073750993;
306  return FALSE;
307  }
308  *lval /= rval;
309  break;
310  }
311 
312  case '%':
313  {
314  if (rval == 0)
315  {
316  // FIXME: Localize
317  ConErrPuts(_T("Division by zero error.\n"));
318  nErrorLevel = 0x400023D1; // 1073750993;
319  return FALSE;
320  }
321  *lval %= rval;
322  break;
323  }
324 
325  case '+':
326  *lval += rval;
327  break;
328  case '-':
329  *lval -= rval;
330  break;
331  case '&':
332  *lval &= rval;
333  break;
334  case '^':
335  *lval ^= rval;
336  break;
337  case '|':
338  *lval |= rval;
339  break;
340 
341  default:
343  nErrorLevel = 0x400023CE; // 1073750990;
344  return FALSE;
345  }
346  return TRUE;
347 }
348 
349 static BOOL
350 seta_stmt(LPCTSTR* p_, INT* result);
351 
352 static BOOL
354 {
355  LPCTSTR p = *p_;
356  INT rval;
357 
358  if (*p == _T('('))
359  {
360  p = skip_ws(p + 1);
361  if (!seta_stmt(&p, &rval))
362  return FALSE;
363  if (*p++ != _T(')'))
364  {
366  nErrorLevel = 0x400023CC; // 1073750988;
367  return FALSE;
368  }
369  *result = rval;
370  }
371  else if (_istdigit(*p))
372  {
373  errno = 0;
374  rval = _tcstol(p, (LPTSTR*)&p, 0);
375 
376  /* Check for overflow / underflow */
377  if (errno == ERANGE)
378  {
379  // FIXME: Localize
380  ConErrPuts(_T("Invalid number. Numbers are limited to 32-bits of precision.\n"));
381  nErrorLevel = 0x400023D0; // 1073750992;
382  return FALSE;
383  }
384  /*
385  * _tcstol() stopped at the first non-digit character. If it's not a whitespace,
386  * or if it's the start of a possible identifier, this means the number being
387  * interpreted was invalid.
388  */
389  else if (*p && !_istspace(*p) && __iscsymf(*p))
390  {
391  // FIXME: Localize
392  ConErrPuts(_T("Invalid number. Numeric constants are either decimal (42), hexadecimal (0x2A), or octal (052).\n"));
393  nErrorLevel = 0x400023CF; // 1073750991;
394  return FALSE;
395  }
396  *result = rval;
397  }
398  else if (__iscsymf(*p))
399  {
400  LPTSTR ident;
401  INT identlen;
402  PARSE_IDENT(ident, identlen, p);
404  }
405  else
406  {
408  nErrorLevel = 0x400023CD; // 1073750989;
409  return FALSE;
410  }
411  *p_ = skip_ws(p);
412  return TRUE;
413 }
414 
415 static BOOL
417 {
418  LPCTSTR p = *p_;
419  TCHAR op = 0;
420  INT rval;
421 
422  if (_tcschr(_T("!~-+"), *p))
423  {
424  op = *p;
425  p = skip_ws(p + 1);
426 
427  if (!seta_mulTerm(&p, &rval))
428  return FALSE;
429 
430  switch (op)
431  {
432  case '!':
433  rval = !rval;
434  break;
435  case '~':
436  rval = ~rval;
437  break;
438  case '-':
439  rval = -rval;
440  break;
441 #if 0
442  case '+':
443  rval = rval;
444  break;
445 #endif
446  }
447  }
448  else
449  {
450  if (!seta_unaryTerm(&p, &rval))
451  return FALSE;
452  }
453 
454  *result = rval;
455  *p_ = p;
456  return TRUE;
457 }
458 
459 static BOOL
460 seta_ltorTerm(LPCTSTR* p_, INT* result, LPCTSTR ops, BOOL (*subTerm)(LPCTSTR*,INT*))
461 {
462  LPCTSTR p = *p_;
463  INT lval;
464 
465  /* Evaluate the left-hand side */
466  if (!subTerm(&p, &lval))
467  return FALSE;
468 
469  while (*p && _tcschr(ops, *p))
470  {
471  INT rval;
472  TCHAR op = *p;
473 
474  p = skip_ws(p + 1);
475 
476  /* Evaluate the immediate right-hand side */
477  if (!subTerm(&p, &rval))
478  return FALSE;
479 
480  /* This becomes the new left-hand side for the next iteration */
481  if (!calc(&lval, op, rval))
482  return FALSE;
483  }
484 
485  *result = lval;
486  *p_ = p;
487  return TRUE;
488 }
489 
490 static BOOL
492 {
493  return seta_ltorTerm(p_, result, _T("*/%"), seta_mulTerm);
494 }
495 
496 static BOOL
498 {
499  return seta_ltorTerm(p_, result, _T("+-"), seta_addTerm);
500 }
501 
502 static BOOL
504 {
505  LPCTSTR p = *p_;
506  INT lval;
507 
508  /* Evaluate the left-hand side */
509  if (!seta_logShiftTerm(&p, &lval))
510  return FALSE;
511 
512  /* Handle << >> operators */
513  while (*p && _tcschr(_T("<>"), *p))
514  {
515  INT rval;
516  TCHAR op = *p;
517 
518  /* Check whether the next non-whitespace character is the same operator */
519  p = skip_ws(p + 1);
520  if (*p != op)
521  break;
522 
523  /* Skip it */
524  p = skip_ws(p + 1);
525 
526  /* Evaluate the immediate right-hand side */
527  if (!seta_logShiftTerm(&p, &rval))
528  return FALSE;
529 
530  /* This becomes the new left-hand side for the next iteration */
531  switch (op)
532  {
533  case '<':
534  {
535  /* Shift left has to be a positive number, 0-31 otherwise 0 is returned,
536  * which differs from the compiler (for example gcc) so being explicit. */
537  if (rval < 0 || rval >= (8 * sizeof(lval)))
538  lval = 0;
539  else
540  lval <<= rval;
541  break;
542  }
543 
544  case '>':
545  lval >>= rval;
546  break;
547 
548  default:
550  nErrorLevel = 0x400023CE; // 1073750990;
551  return FALSE;
552  }
553  }
554 
555  *result = lval;
556  *p_ = p;
557  return TRUE;
558 }
559 
560 static BOOL
562 {
563  return seta_ltorTerm(p_, result, _T("&"), seta_bitAndTerm);
564 }
565 
566 static BOOL
568 {
569  return seta_ltorTerm(p_, result, _T("^"), seta_bitExclOrTerm);
570 }
571 
572 static BOOL
574 {
575  return seta_ltorTerm(p_, result, _T("|"), seta_bitOrTerm);
576 }
577 
578 static BOOL
580 {
581  LPCTSTR p = *p_;
582  LPTSTR ident;
583  TCHAR op = 0;
584  INT identlen, exprval;
585 
586  PARSE_IDENT(ident, identlen, p);
587  if (identlen)
588  {
589  p = skip_ws(p);
590 
591  /* Handle = assignment */
592  if (*p == _T('='))
593  {
594  op = *p;
595  p = skip_ws(p + 1);
596  }
597  /* Handle *= /= %= += -= &= ^= |= assignments */
598  else if (_tcschr(_T("*/%+-&^|"), *p))
599  {
600  op = *p;
601 
602  /* Find the '=', there may be some spaces before it */
603  p = skip_ws(p + 1);
604  if (*p != _T('='))
605  {
606  op = 0;
607  goto evaluate;
608  }
609 
610  /* Skip it */
611  p = skip_ws(p + 1);
612  }
613  /* Handle <<= >>= assignments */
614  else if (_tcschr(_T("<>"), *p))
615  {
616  op = *p;
617 
618  /* Check whether the next non-whitespace character is the same operator */
619  p = skip_ws(p + 1);
620  if (*p != op)
621  {
622  op = 0;
623  goto evaluate;
624  }
625 
626  /* Find the '=', there may be some spaces before it */
627  p = skip_ws(p + 1);
628  if (*p != _T('='))
629  {
630  op = 0;
631  goto evaluate;
632  }
633 
634  /* Skip it */
635  p = skip_ws(p + 1);
636  }
637  }
638 
639 evaluate:
640  /* Allow to chain multiple assignments, such as: a=b=1 */
641  if (ident && op)
642  {
643  INT identval;
644  LPTSTR buf;
645 
646  if (!seta_assignment(&p, &exprval))
647  return FALSE;
648 
649  identval = seta_identval(ident);
650 
651  switch (op)
652  {
653  /* Handle = assignment */
654  case '=':
655  identval = exprval;
656  break;
657 
658  /* Handle <<= assignment */
659  case '<':
660  {
661  /* Shift left has to be a positive number, 0-31 otherwise 0 is returned,
662  * which differs from the compiler (for example gcc) so being explicit. */
663  if (exprval < 0 || exprval >= (8 * sizeof(identval)))
664  identval = 0;
665  else
666  identval <<= exprval;
667  break;
668  }
669 
670  /* Handle >>= assignment */
671  case '>':
672  identval >>= exprval;
673  break;
674 
675  /* Other assignments */
676  default:
677  if (!calc(&identval, op, exprval))
678  return FALSE;
679  }
680 
681  buf = (LPTSTR)alloca(32 * sizeof(TCHAR));
682  _sntprintf(buf, 32, _T("%i"), identval);
683  SetEnvironmentVariable(ident, buf); // TODO FIXME - check return value
684  exprval = identval;
685  }
686  else
687  {
688  /* Restore p in case we found an identifier but not an operator */
689  p = *p_;
690  if (!seta_expr(&p, &exprval))
691  return FALSE;
692  }
693 
694  *result = exprval;
695  *p_ = p;
696  return TRUE;
697 }
698 
699 static BOOL
701 {
702  LPCTSTR p = *p_;
703  INT rval;
704 
705  if (!seta_assignment(&p, &rval))
706  return FALSE;
707 
708  /* Loop over each statement */
709  while (*p == _T(','))
710  {
711  p = skip_ws(p + 1);
712 
713  if (!seta_assignment(&p, &rval))
714  return FALSE;
715  }
716 
717  *result = rval;
718  *p_ = p;
719  return TRUE;
720 }
721 
722 static BOOL
724 {
725  INT rval;
726 
727  if (!*p)
728  {
730  nErrorLevel = 1;
731  return FALSE;
732  }
733  if (!seta_stmt(&p, &rval))
734  return FALSE;
735 
736  /* If unparsed data remains, fail and bail out */
737  if (*p)
738  {
739  ConErrResPuts(STRING_SYNTAX_COMMAND_INCORRECT); // Actually syntax error / missing operand.
740  nErrorLevel = 0x400023CE; // 1073750990;
741  return FALSE;
742  }
743 
744  /* Echo the result of the evaluation only in interactive (non-batch) mode */
745  if (!bc)
746  ConOutPrintf(_T("%i"), rval);
747 
748  return TRUE;
749 }
750 
751 #endif
INT nErrorLevel
Definition: cmd.c:158
#define _istdigit
Definition: tchar.h:1494
const CHAR * LPCTSTR
Definition: xmlstorage.h:193
#define STRING_INVALID_OPERAND
Definition: resource.h:215
GLenum GLuint GLenum GLsizei const GLchar * buf
Definition: glext.h:7751
#define TRUE
Definition: types.h:120
VOID ConOutChar(TCHAR c)
Definition: console.c:123
#define _tcstol
Definition: tchar.h:594
#define ConErrResPuts(uID)
Definition: console.h:39
static BOOL seta_eval(LPCTSTR expr)
Definition: set.c:723
static VOID ConInString(LPWSTR lpInput, DWORD dwLength)
Definition: label.c:56
static BOOL seta_mulTerm(LPCTSTR *p_, INT *result)
Definition: set.c:416
#define ARRAYSIZE(array)
Definition: filtermapper.c:47
static LPCTSTR skip_ws(LPCTSTR p)
Definition: set.c:48
#define ConOutPrintf(szStr,...)
Definition: console.h:42
VOID ConOutResPaging(BOOL StartPaging, UINT resID)
Definition: console.c:182
GLuint GLuint end
Definition: gl.h:1545
static INT seta_identval(LPCTSTR ident)
Definition: set.c:281
int errno
static VOID StripQuotes(LPSTR in)
Definition: cmdcons.c:116
int32_t INT
Definition: typedefs.h:58
Definition: query.h:86
CHAR * LPTSTR
Definition: xmlstorage.h:192
#define alloca
Definition: malloc.h:361
#define STRING_SYNTAX_COMMAND_INCORRECT
Definition: resource.h:218
#define STRING_SET_ENV_ERROR
Definition: resource.h:55
#define FALSE
Definition: types.h:117
unsigned int BOOL
Definition: ntddk_ex.h:94
#define ConErrResPrintf(uID,...)
Definition: console.h:51
#define _tcsnicmp
Definition: xmlstorage.h:207
LPCTSTR GetEnvVarOrSpecial(LPCTSTR varName)
Definition: cmd.c:900
static BOOL seta_stmt(LPCTSTR *p_, INT *result)
Definition: set.c:700
static BOOL seta_logShiftTerm(LPCTSTR *p_, INT *result)
Definition: set.c:497
size_t __cdecl _tcslen(const _TCHAR *str)
Definition: tcslen.h:9
unsigned char BOOLEAN
static LPTSTR GetQuotedString(TCHAR *p)
Definition: set.c:58
smooth NULL
Definition: ftsmooth.c:416
static BOOL seta_expr(LPCTSTR *p_, INT *result)
Definition: set.c:573
#define __iscsym(_c)
Definition: ctype.h:691
#define STRING_EXPECTED_NUMBER_OR_VARIABLE
Definition: resource.h:217
_TCHAR * _tcschr(const _TCHAR *s, _XINT c)
Definition: tcschr.h:4
_In_ ULONG _In_ ULONG_PTR ident
Definition: winddi.h:3993
static BOOL seta_ltorTerm(LPCTSTR *p_, INT *result, LPCTSTR ops, BOOL(*subTerm)(LPCTSTR *, INT *))
Definition: set.c:460
Definition: batch.h:19
char TCHAR
Definition: xmlstorage.h:189
#define _T(x)
Definition: vfdio.h:22
BATCH_TYPE BatType
Definition: batch.c:66
static BOOL seta_addTerm(LPCTSTR *p_, INT *result)
Definition: set.c:491
GLfloat param
Definition: glext.h:5796
VOID ConErrPuts(LPTSTR szText)
Definition: tee.c:59
UINT op
Definition: effect.c:224
#define ERANGE
Definition: acclib.h:92
static BOOL seta_assignment(LPCTSTR *p_, INT *result)
Definition: set.c:579
static BOOL seta_unaryTerm(LPCTSTR *p_, INT *result)
Definition: set.c:353
float rval
Definition: cylfrac.c:48
#define _sntprintf
Definition: xmlstorage.h:201
#define SetEnvironmentVariable
Definition: winbase.h:3748
static INT ident_len(LPCTSTR p)
Definition: set.c:259
static BOOL calc(INT *lval, TCHAR op, INT rval)
Definition: set.c:291
#define STRING_SET_HELP
Definition: resource.h:171
#define PARSE_IDENT(ident, identlen, p)
Definition: set.c:271
LPSTR WINAPI GetEnvironmentStrings(void)
#define STRING_EXPECTED_CLOSE_PAREN
Definition: resource.h:216
_TCHAR * _tcsrchr(const _TCHAR *s, _XINT c)
Definition: tcsrchr.h:4
#define _istspace
Definition: tchar.h:1504
PBATCH_CONTEXT bc
Definition: batch.c:67
static BOOL seta_bitExclOrTerm(LPCTSTR *p_, INT *result)
Definition: set.c:561
static BOOL seta_bitOrTerm(LPCTSTR *p_, INT *result)
Definition: set.c:567
GLfloat GLfloat p
Definition: glext.h:8902
#define FreeEnvironmentStrings
Definition: winbase.h:3636
GLuint64EXT * result
Definition: glext.h:11304
int _tcsncmp(const _TCHAR *s1, const _TCHAR *s2, size_t n)
Definition: tcsncmp.h:9
INT cmd_set(LPTSTR param)
Definition: set.c:72
VOID ConOutPuts(LPTSTR szText)
Definition: tee.c:27
#define __iscsymf(_c)
Definition: ctype.h:690
static BOOL seta_bitAndTerm(LPCTSTR *p_, INT *result)
Definition: set.c:503