ReactOS  0.4.15-dev-980-ge160524
parser.c
Go to the documentation of this file.
1 /*
2  * PARSER.C - command parsing.
3  */
4 
5 #include "precomp.h"
6 
7 /* Enable this define for "buggy" Windows' CMD command echoer compatibility */
8 #define MSCMD_ECHO_COMMAND_COMPAT
9 
10 /*
11  * Parser debugging support. These flags are global so that their values can be
12  * modified at runtime from a debugger. They correspond to the public Windows'
13  * cmd!fDumpTokens and cmd!fDumpParse booleans.
14  * (Same names are used for compatibility as they are documented online.)
15  */
18 
19 #define C_OP_LOWEST C_MULTI
20 #define C_OP_HIGHEST C_PIPE
21 static const TCHAR OpString[][3] = { _T("&"), _T("||"), _T("&&"), _T("|") };
22 
23 static const TCHAR RedirString[][3] = { _T("<"), _T(">"), _T(">>") };
24 
25 static const TCHAR *const IfOperatorString[] =
26 {
27  /* Standard */
28  _T("errorlevel"),
29  _T("exist"),
30 
31  /* Extended */
32  _T("cmdextversion"),
33  _T("defined"),
34 #define IF_MAX_UNARY IF_DEFINED
35 
36  /* Standard */
37  _T("=="),
38 
39  /* Extended */
40  _T("equ"),
41  _T("neq"),
42  _T("lss"),
43  _T("leq"),
44  _T("gtr"),
45  _T("geq"),
46 #define IF_MAX_COMPARISON IF_GEQ
47 };
48 
49 static BOOL IsSeparator(TCHAR Char)
50 {
51  return _istspace(Char) || (Char && _tcschr(STANDARD_SEPS, Char));
52 }
53 
54 enum
55 {
62 };
63 
64 /* Scratch buffer for temporary command substitutions / expansions */
66 
67 /*static*/ BOOL bParseError;
70 static TCHAR *ParsePos;
71 static TCHAR CurChar;
72 
74 static int CurrentTokenType;
75 static int InsideBlock;
76 
77 static TCHAR ParseChar(void)
78 {
79  TCHAR Char;
80 
81  if (bParseError)
82  return (CurChar = 0);
83 
84 restart:
85  /*
86  * Although CRs can be injected into a line via an environment
87  * variable substitution, the parser ignores them - they won't
88  * even separate tokens.
89  */
90  do
91  {
92  Char = *ParsePos++;
93  }
94  while (Char == _T('\r'));
95 
96  if (!Char)
97  {
98  ParsePos--;
100  {
101  if (!ReadLine(ParseLine, TRUE))
102  {
103  /* ^C pressed, or line was too long */
104  bParseError = TRUE;
105  }
106  else if (*(ParsePos = ParseLine))
107  {
108  goto restart;
109  }
110  }
111  }
112  return (CurChar = Char);
113 }
114 
116 {
117  /* Only display the first error we encounter */
118  if (!bParseError)
119  error_syntax(s);
120  bParseError = TRUE;
121 }
122 
123 static __inline VOID ParseError(VOID)
124 {
126 }
127 
128 /*
129  * Yes, cmd has a Lexical Analyzer. Whenever the parser gives an "xxx was
130  * unexpected at this time." message, it shows what the last token read was.
131  */
132 static int ParseToken(TCHAR ExtraEnd, TCHAR *Separators)
133 {
135  TCHAR Char;
136  int Type;
137  BOOL bInQuote = FALSE;
138 
139  for (Char = CurChar; Char && Char != _T('\n'); Char = ParseChar())
140  {
141  bInQuote ^= (Char == _T('"'));
142  if (!bInQuote)
143  {
144  if (Separators != NULL)
145  {
146  if (_istspace(Char) || _tcschr(Separators, Char))
147  {
148  /* Skip leading separators */
149  if (Out == CurrentToken)
150  continue;
151  break;
152  }
153  }
154 
155  /* Check for numbered redirection */
156  if ((Char >= _T('0') && Char <= _T('9') &&
157  (ParsePos == &ParseLine[1] || IsSeparator(ParsePos[-2]))
158  && (*ParsePos == _T('<') || *ParsePos == _T('>'))))
159  {
160  break;
161  }
162 
163  if (Char == ExtraEnd)
164  break;
165  if (InsideBlock && Char == _T(')'))
166  break;
167  if (_tcschr(_T("&|<>"), Char))
168  break;
169 
170  if (Char == _T('^'))
171  {
172  Char = ParseChar();
173  /* Eat up a \n, allowing line continuation */
174  if (Char == _T('\n'))
175  Char = ParseChar();
176  /* Next character is a forced literal */
177  }
178  }
179  if (Out == &CurrentToken[CMDLINE_LENGTH - 1])
180  break;
181  *Out++ = Char;
182  }
183 
184  /* Check if we got at least one character before reaching a special one.
185  * If so, return them and leave the special for the next call. */
186  if (Out != CurrentToken)
187  {
188  Type = TOK_NORMAL;
189  }
190  else if (Char == _T('('))
191  {
193  *Out++ = Char;
194  ParseChar();
195  }
196  else if (Char == _T(')'))
197  {
199  *Out++ = Char;
200  ParseChar();
201  }
202  else if (Char == _T('&') || Char == _T('|'))
203  {
204  Type = TOK_OPERATOR;
205  *Out++ = Char;
206  Char = ParseChar();
207  /* check for && or || */
208  if (Char == Out[-1])
209  {
210  *Out++ = Char;
211  ParseChar();
212  }
213  }
214  else if ((Char >= _T('0') && Char <= _T('9'))
215  || (Char == _T('<') || Char == _T('>')))
216  {
218  if (Char >= _T('0') && Char <= _T('9'))
219  {
220  *Out++ = Char;
221  Char = ParseChar();
222  }
223  *Out++ = Char;
224  Char = ParseChar();
225  if (Char == Out[-1])
226  {
227  /* Strangely, the tokenizer allows << as well as >>... (it
228  * will cause an error when trying to parse it though) */
229  *Out++ = Char;
230  Char = ParseChar();
231  }
232  if (Char == _T('&'))
233  {
234  *Out++ = Char;
235  while (IsSeparator(Char = ParseChar()))
236  ;
237  if (Char >= _T('0') && Char <= _T('9'))
238  {
239  *Out++ = Char;
240  ParseChar();
241  }
242  }
243  }
244  else
245  {
246  Type = TOK_END;
247  }
248  *Out = _T('\0');
249 
250  /* Debugging support */
251  if (fDumpTokens)
252  ConOutPrintf(_T("ParseToken: (%d) '%s'\n"), Type, CurrentToken);
253 
254  return (CurrentTokenType = Type);
255 }
256 
258 {
259  TCHAR *Tok = CurrentToken;
260  BYTE Number;
261  REDIR_MODE RedirMode;
262  REDIRECTION *Redir;
263 
264  if (*Tok >= _T('0') && *Tok <= _T('9'))
265  Number = *Tok++ - _T('0');
266  else
267  Number = *Tok == _T('<') ? 0 : 1;
268 
269  if (*Tok++ == _T('<'))
270  {
271  RedirMode = REDIR_READ;
272  if (*Tok == _T('<'))
273  goto fail;
274  }
275  else
276  {
277  RedirMode = REDIR_WRITE;
278  if (*Tok == _T('>'))
279  {
280  RedirMode = REDIR_APPEND;
281  Tok++;
282  }
283  }
284 
285  if (!*Tok)
286  {
287  /* The file name was not part of this token, so it'll be the next one */
289  goto fail;
290  Tok = CurrentToken;
291  }
292 
293  /* If a redirection for this handle number already exists, delete it */
294  while ((Redir = *List))
295  {
296  if (Redir->Number == Number)
297  {
298  *List = Redir->Next;
299  cmd_free(Redir);
300  continue;
301  }
302  List = &Redir->Next;
303  }
304 
305  Redir = cmd_alloc(FIELD_OFFSET(REDIRECTION, Filename[_tcslen(Tok) + 1]));
306  if (!Redir)
307  {
308  WARN("Cannot allocate memory for Redir!\n");
309  goto fail;
310  }
311  Redir->Next = NULL;
313  Redir->Number = Number;
314  Redir->Mode = RedirMode;
315  _tcscpy(Redir->Filename, Tok);
316  *List = Redir;
317  return TRUE;
318 
319 fail:
320  ParseError();
322  *List = NULL;
323  return FALSE;
324 }
325 
326 static PARSED_COMMAND *ParseCommandOp(int OpType);
327 
328 /* Parse a parenthesized block */
330 {
331  PARSED_COMMAND *Cmd, *Sub, **NextPtr;
332 
333  Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
334  if (!Cmd)
335  {
336  WARN("Cannot allocate memory for Cmd!\n");
337  ParseError();
338  FreeRedirection(RedirList);
339  return NULL;
340  }
341  Cmd->Type = C_BLOCK;
342  Cmd->Next = NULL;
343  Cmd->Subcommands = NULL;
344  Cmd->Redirections = RedirList;
345 
346  /* Read the block contents */
347  NextPtr = &Cmd->Subcommands;
348  InsideBlock++;
349  while (1)
350  {
352  if (Sub)
353  {
354  *NextPtr = Sub;
355  NextPtr = &Sub->Next;
356  }
357  else if (bParseError)
358  {
359  InsideBlock--;
360  FreeCommand(Cmd);
361  return NULL;
362  }
363 
365  break;
366 
367  /* Skip past the \n */
368  ParseChar();
369  }
370  InsideBlock--;
371 
372  /* Process any trailing redirections */
374  {
375  if (!ParseRedirection(&Cmd->Redirections))
376  {
377  FreeCommand(Cmd);
378  return NULL;
379  }
380  }
381  return Cmd;
382 }
383 
384 /* Parse an IF statement */
385 static PARSED_COMMAND *ParseIf(void)
386 {
388 
389  Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
390  if (!Cmd)
391  {
392  WARN("Cannot allocate memory for Cmd!\n");
393  ParseError();
394  return NULL;
395  }
396  memset(Cmd, 0, sizeof(PARSED_COMMAND));
397  Cmd->Type = C_IF;
398 
399  if (bEnableExtensions && (_tcsicmp(CurrentToken, _T("/I")) == 0))
400  {
401  Cmd->If.Flags |= IFFLAG_IGNORECASE;
403  }
404  if (_tcsicmp(CurrentToken, _T("not")) == 0)
405  {
406  Cmd->If.Flags |= IFFLAG_NEGATE;
408  }
409 
411  goto error;
412 
413  /* Check for unary operators */
414  for (; Cmd->If.Operator <= IF_MAX_UNARY; Cmd->If.Operator++)
415  {
416  /* Skip the extended operators if the extensions are disabled */
417  if (!bEnableExtensions && (Cmd->If.Operator >= IF_CMDEXTVERSION))
418  continue;
419 
420  if (_tcsicmp(CurrentToken, IfOperatorString[Cmd->If.Operator]) == 0)
421  {
423  goto error;
424  Cmd->If.RightArg = cmd_dup(CurrentToken);
425  goto condition_done;
426  }
427  }
428 
429  /* It must be a two-argument (comparison) operator. It could be ==, so
430  * the equals sign can't be treated as whitespace here. */
431  Cmd->If.LeftArg = cmd_dup(CurrentToken);
432  ParseToken(0, _T(",;"));
433 
434  /* The right argument can come immediately after == */
435  if (_tcsnicmp(CurrentToken, _T("=="), 2) == 0 && CurrentToken[2])
436  {
437  Cmd->If.RightArg = cmd_dup(&CurrentToken[2]);
438  goto condition_done;
439  }
440 
441  // Cmd->If.Operator == IF_MAX_UNARY + 1;
442  for (; Cmd->If.Operator <= IF_MAX_COMPARISON; Cmd->If.Operator++)
443  {
444  /* Skip the extended operators if the extensions are disabled */
445  if (!bEnableExtensions && (Cmd->If.Operator >= IF_EQU)) // (Cmd->If.Operator > IF_STRINGEQ)
446  continue;
447 
448  if (_tcsicmp(CurrentToken, IfOperatorString[Cmd->If.Operator]) == 0)
449  {
451  goto error;
452  Cmd->If.RightArg = cmd_dup(CurrentToken);
453  goto condition_done;
454  }
455  }
456  goto error;
457 
458 condition_done:
459  Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST);
460  if (Cmd->Subcommands == NULL)
461  goto error;
462  if (_tcsicmp(CurrentToken, _T("else")) == 0)
463  {
464  Cmd->Subcommands->Next = ParseCommandOp(C_OP_LOWEST);
465  if (Cmd->Subcommands->Next == NULL)
466  goto error;
467  }
468 
469  return Cmd;
470 
471 error:
472  FreeCommand(Cmd);
473  ParseError();
474  return NULL;
475 }
476 
477 /*
478  * Parse a FOR command.
479  * Syntax is: FOR [options] %var IN (list) DO command
480  */
482 {
484  TCHAR* List = TempBuf;
485  TCHAR *Pos = List;
486 
487  Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
488  if (!Cmd)
489  {
490  WARN("Cannot allocate memory for Cmd!\n");
491  ParseError();
492  return NULL;
493  }
494  memset(Cmd, 0, sizeof(PARSED_COMMAND));
495  Cmd->Type = C_FOR;
496 
497  /* Skip the extended FOR syntax if extensions are disabled */
498  if (!bEnableExtensions)
499  goto parseForBody;
500 
501  while (1)
502  {
503  if (_tcsicmp(CurrentToken, _T("/D")) == 0)
504  {
505  Cmd->For.Switches |= FOR_DIRS;
506  }
507  else if (_tcsicmp(CurrentToken, _T("/F")) == 0)
508  {
509  Cmd->For.Switches |= FOR_F;
510  if (!Cmd->For.Params)
511  {
513  if (CurrentToken[0] == _T('/') || CurrentToken[0] == _T('%'))
514  break;
515  Cmd->For.Params = cmd_dup(CurrentToken);
516  }
517  }
518  else if (_tcsicmp(CurrentToken, _T("/L")) == 0)
519  {
520  Cmd->For.Switches |= FOR_LOOP;
521  }
522  else if (_tcsicmp(CurrentToken, _T("/R")) == 0)
523  {
524  Cmd->For.Switches |= FOR_RECURSIVE;
525  if (!Cmd->For.Params)
526  {
528  if (CurrentToken[0] == _T('/') || CurrentToken[0] == _T('%'))
529  break;
531  Cmd->For.Params = cmd_dup(CurrentToken);
532  }
533  }
534  else
535  {
536  break;
537  }
538 
540  }
541 
542  /* Make sure there aren't two different switches specified
543  * at the same time, unless they're /D and /R */
544  if ((Cmd->For.Switches & (Cmd->For.Switches - 1)) != 0
545  && Cmd->For.Switches != (FOR_DIRS | FOR_RECURSIVE))
546  {
547  goto error;
548  }
549 
550 parseForBody:
551 
552  /* Variable name should be % and just one other character */
553  if (CurrentToken[0] != _T('%') || _tcslen(CurrentToken) != 2)
554  goto error;
555  Cmd->For.Variable = CurrentToken[1];
556 
558  if (_tcsicmp(CurrentToken, _T("in")) != 0)
559  goto error;
560 
562  goto error;
563 
564  while (1)
565  {
566  /* Pretend we're inside a block so the tokenizer will stop on ')' */
567  InsideBlock++;
569  InsideBlock--;
570 
572  break;
573 
574  if (CurrentTokenType == TOK_END)
575  {
576  /* Skip past the \n */
577  ParseChar();
578  continue;
579  }
580 
582  goto error;
583 
584  if (Pos != List)
585  *Pos++ = _T(' ');
586 
588  goto error;
590  }
591  *Pos = _T('\0');
592  Cmd->For.List = cmd_dup(List);
593 
595  if (_tcsicmp(CurrentToken, _T("do")) != 0)
596  goto error;
597 
598  Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST);
599  if (Cmd->Subcommands == NULL)
600  goto error;
601 
602  return Cmd;
603 
604 error:
605  FreeCommand(Cmd);
606  ParseError();
607  return NULL;
608 }
609 
610 /* Parse a REM command */
612 {
613  /* "Ignore" the rest of the line.
614  * (Line continuations will still be parsed, though.) */
615  while (ParseToken(0, NULL) != TOK_END)
616  ;
617  return NULL;
618 }
619 
621 {
622  TCHAR ParsedLine[CMDLINE_LENGTH];
624  PARSED_COMMAND *(*Func)(void);
625 
626  TCHAR *Pos = _stpcpy(ParsedLine, CurrentToken) + 1;
627  DWORD_PTR TailOffset = Pos - ParsedLine;
628 
629  /* Check for special forms */
630  if ((Func = ParseFor, _tcsicmp(ParsedLine, _T("for")) == 0) ||
631  (Func = ParseIf, _tcsicmp(ParsedLine, _T("if")) == 0) ||
632  (Func = ParseRem, _tcsicmp(ParsedLine, _T("rem")) == 0))
633  {
635  /* Do special parsing only if it's not followed by /? */
636  if (_tcscmp(CurrentToken, _T("/?")) != 0)
637  {
638  if (RedirList)
639  {
640  ParseError();
641  FreeRedirection(RedirList);
642  return NULL;
643  }
644  return Func();
645  }
646  Pos = _stpcpy(Pos, _T(" /?"));
647  }
648 
649  /* Now get the tail */
650  while (1)
651  {
652  ParseToken(0, NULL);
654  {
655  if (Pos + _tcslen(CurrentToken) >= &ParsedLine[CMDLINE_LENGTH])
656  {
657  ParseError();
658  FreeRedirection(RedirList);
659  return NULL;
660  }
662  }
663  else if (CurrentTokenType == TOK_REDIRECTION)
664  {
665  if (!ParseRedirection(&RedirList))
666  return NULL;
667  }
668  else
669  {
670  break;
671  }
672  }
673  *Pos++ = _T('\0');
674 
675  Cmd = cmd_alloc(FIELD_OFFSET(PARSED_COMMAND, Command.First[Pos - ParsedLine]));
676  if (!Cmd)
677  {
678  WARN("Cannot allocate memory for Cmd!\n");
679  ParseError();
680  FreeRedirection(RedirList);
681  return NULL;
682  }
683  Cmd->Type = C_COMMAND;
684  Cmd->Next = NULL;
685  Cmd->Subcommands = NULL;
686  Cmd->Redirections = RedirList;
687  memcpy(Cmd->Command.First, ParsedLine, (Pos - ParsedLine) * sizeof(TCHAR));
688  Cmd->Command.Rest = Cmd->Command.First + TailOffset;
689  return Cmd;
690 }
691 
693 {
694  REDIRECTION *RedirList = NULL;
695  int Type;
696 
697  while (IsSeparator(CurChar))
698  {
699  if (CurChar == _T('\n'))
700  return NULL;
701  ParseChar();
702  }
703 
704  if (!CurChar)
705  return NULL;
706 
707  if (CurChar == _T(':'))
708  {
709  /* "Ignore" the rest of the line.
710  * (Line continuations will still be parsed, though.) */
711  while (ParseToken(0, NULL) != TOK_END)
712  ;
713  return NULL;
714  }
715 
716  if (CurChar == _T('@'))
717  {
719  ParseChar();
720  Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
721  if (!Cmd)
722  {
723  WARN("Cannot allocate memory for Cmd!\n");
724  ParseError();
725  return NULL;
726  }
727  Cmd->Type = C_QUIET;
728  Cmd->Next = NULL;
729  /* @ acts like a unary operator with low precedence,
730  * so call the top-level parser */
731  Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST);
732  Cmd->Redirections = NULL;
733  return Cmd;
734  }
735 
736  /* Process leading redirections and get the head of the command */
737  while ((Type = ParseToken(_T('('), STANDARD_SEPS)) == TOK_REDIRECTION)
738  {
739  if (!ParseRedirection(&RedirList))
740  return NULL;
741  }
742 
743  if (Type == TOK_NORMAL)
744  return ParseCommandPart(RedirList);
745  else if (Type == TOK_BEGIN_BLOCK)
746  return ParseBlock(RedirList);
747  else if (Type == TOK_END_BLOCK && !RedirList)
748  return NULL;
749 
750  ParseError();
751  FreeRedirection(RedirList);
752  return NULL;
753 }
754 
755 static PARSED_COMMAND *ParseCommandOp(int OpType)
756 {
757  PARSED_COMMAND *Cmd, *Left, *Right;
758 
759  if (OpType == C_OP_HIGHEST)
760  Cmd = ParsePrimary();
761  else
762  Cmd = ParseCommandOp(OpType + 1);
763 
764  if (Cmd && !_tcscmp(CurrentToken, OpString[OpType - C_OP_LOWEST]))
765  {
766  Left = Cmd;
767  Right = ParseCommandOp(OpType);
768  if (!Right)
769  {
770  if (!bParseError)
771  {
772  /* & is allowed to have an empty RHS */
773  if (OpType == C_MULTI)
774  return Left;
775  ParseError();
776  }
777  FreeCommand(Left);
778  return NULL;
779  }
780 
781  Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
782  if (!Cmd)
783  {
784  WARN("Cannot allocate memory for Cmd!\n");
785  ParseError();
786  FreeCommand(Left);
787  FreeCommand(Right);
788  return NULL;
789  }
790  Cmd->Type = OpType;
791  Cmd->Next = NULL;
792  Cmd->Redirections = NULL;
793  Cmd->Subcommands = Left;
794  Left->Next = Right;
795  Right->Next = NULL;
796  }
797 
798  return Cmd;
799 }
800 
801 VOID
802 DumpCommand(PARSED_COMMAND *Cmd, ULONG SpacePad);
803 
806 {
808 
809  if (Line)
810  {
811  if (!SubstituteVars(Line, ParseLine, _T('%')))
812  return NULL;
814  }
815  else
816  {
817  if (!ReadLine(ParseLine, FALSE))
818  return NULL;
820  }
821  bParseError = FALSE;
823  CurChar = _T(' ');
824 
826  if (Cmd)
827  {
828  bIgnoreEcho = FALSE;
829 
830  if (CurrentTokenType != TOK_END)
831  ParseError();
832  if (bParseError)
833  {
834  FreeCommand(Cmd);
835  return NULL;
836  }
837 
838  /* Debugging support */
839  if (fDumpParse)
840  DumpCommand(Cmd, 0);
841  }
842  else
843  {
844  bIgnoreEcho = TRUE;
845  }
846  return Cmd;
847 }
848 
849 
850 /*
851  * This function is similar to EchoCommand(), but is used
852  * for dumping the command tree for debugging purposes.
853  */
854 static VOID
855 DumpRedir(REDIRECTION* Redirections)
856 {
857  REDIRECTION* Redir;
858 
859  if (Redirections)
860 #ifndef MSCMD_ECHO_COMMAND_COMPAT
861  ConOutPuts(_T(" Redir: "));
862 #else
863  ConOutPuts(_T("Redir: "));
864 #endif
865  for (Redir = Redirections; Redir; Redir = Redir->Next)
866  {
867  ConOutPrintf(_T(" %x %s%s"), Redir->Number,
868  RedirString[Redir->Mode], Redir->Filename);
869  }
870 }
871 
872 VOID
874 {
875 /*
876  * This macro is like DumpCommand(Cmd, Pad);
877  * but avoids an extra recursive level.
878  * Note that it can be used ONLY for terminating commands!
879  */
880 #define DUMP(Command, Pad) \
881 do { \
882  Cmd = (Command); \
883  SpacePad = (Pad); \
884  goto dump; \
885 } while (0)
886 
887  PARSED_COMMAND *Sub;
888 
889 dump:
890  /* Space padding */
891  ConOutPrintf(_T("%*s"), SpacePad, _T(""));
892 
893  switch (Cmd->Type)
894  {
895  case C_COMMAND:
896  {
897  /* Generic command name, and Type */
898 #ifndef MSCMD_ECHO_COMMAND_COMPAT
899  ConOutPrintf(_T("Cmd: %s Type: %x"),
900  Cmd->Command.First, Cmd->Type);
901 #else
902  ConOutPrintf(_T("Cmd: %s Type: %x "),
903  Cmd->Command.First, Cmd->Type);
904 #endif
905  /* Arguments */
906  if (Cmd->Command.Rest && *(Cmd->Command.Rest))
907 #ifndef MSCMD_ECHO_COMMAND_COMPAT
908  ConOutPrintf(_T(" Args: `%s'"), Cmd->Command.Rest);
909 #else
910  ConOutPrintf(_T("Args: `%s' "), Cmd->Command.Rest);
911 #endif
912  /* Redirections */
913  DumpRedir(Cmd->Redirections);
914 
915  ConOutChar(_T('\n'));
916  return;
917  }
918 
919  case C_QUIET:
920  {
921 #ifndef MSCMD_ECHO_COMMAND_COMPAT
922  ConOutChar(_T('@'));
923 #else
924  ConOutPuts(_T("@ "));
925 #endif
926  DumpRedir(Cmd->Redirections); // FIXME: Can we have leading redirections??
927  ConOutChar(_T('\n'));
928 
929  /*DumpCommand*/DUMP(Cmd->Subcommands, SpacePad + 2);
930  return;
931  }
932 
933  case C_BLOCK:
934  {
935 #ifndef MSCMD_ECHO_COMMAND_COMPAT
936  ConOutChar(_T('('));
937 #else
938  ConOutPuts(_T("( "));
939 #endif
940  DumpRedir(Cmd->Redirections);
941  ConOutChar(_T('\n'));
942 
943  SpacePad += 2;
944 
945  for (Sub = Cmd->Subcommands; Sub; Sub = Sub->Next)
946  {
947 #if defined(MSCMD_ECHO_COMMAND_COMPAT) && defined(MSCMD_PARSER_BUGS)
948  /*
949  * We will emulate Windows' CMD handling of "CRLF" and "&" multi-command
950  * enumeration within parenthesized command blocks.
951  */
952 
953  if (!Sub->Next)
954  {
955  DumpCommand(Sub, SpacePad);
956  continue;
957  }
958 
959  if (Sub->Type != C_MULTI)
960  {
961  ConOutPrintf(_T("%*s"), SpacePad, _T(""));
962  ConOutPuts(_T("CRLF \n"));
963  DumpCommand(Sub, SpacePad);
964  continue;
965  }
966 
967  /* Now, Sub->Type == C_MULTI */
968 
969  Cmd = Sub;
970 
971  ConOutPrintf(_T("%*s"), SpacePad, _T(""));
972  ConOutPrintf(_T("%s \n"), OpString[Cmd->Type - C_OP_LOWEST]);
973  // FIXME: Can we have redirections on these operator-type commands?
974 
975  SpacePad += 2;
976 
977  Cmd = Cmd->Subcommands;
978  DumpCommand(Cmd, SpacePad);
979  ConOutPrintf(_T("%*s"), SpacePad, _T(""));
980  ConOutPuts(_T("CRLF \n"));
981  DumpCommand(Cmd->Next, SpacePad);
982 
983  // NOTE: Next commands will remain indented.
984 
985 #else
986 
987  /*
988  * If this command is followed by another one, first display "CRLF".
989  * This also emulates the CRLF placement "bug" of Windows' CMD
990  * for the last two commands.
991  */
992  if (Sub->Next)
993  {
994  ConOutPrintf(_T("%*s"), SpacePad, _T(""));
995 #ifndef MSCMD_ECHO_COMMAND_COMPAT
996  ConOutPuts(_T("CRLF\n"));
997 #else
998  ConOutPuts(_T("CRLF \n"));
999 #endif
1000  }
1001  DumpCommand(Sub, SpacePad);
1002 
1003 #endif // defined(MSCMD_ECHO_COMMAND_COMPAT) && defined(MSCMD_PARSER_BUGS)
1004  }
1005 
1006  return;
1007  }
1008 
1009  case C_MULTI:
1010  case C_OR:
1011  case C_AND:
1012  case C_PIPE:
1013  {
1014 #ifndef MSCMD_ECHO_COMMAND_COMPAT
1015  ConOutPrintf(_T("%s\n"), OpString[Cmd->Type - C_OP_LOWEST]);
1016 #else
1017  ConOutPrintf(_T("%s \n"), OpString[Cmd->Type - C_OP_LOWEST]);
1018 #endif
1019  // FIXME: Can we have redirections on these operator-type commands?
1020 
1021  SpacePad += 2;
1022 
1023  Sub = Cmd->Subcommands;
1024  DumpCommand(Sub, SpacePad);
1025  /*DumpCommand*/DUMP(Sub->Next, SpacePad);
1026  return;
1027  }
1028 
1029  case C_IF:
1030  {
1031  ConOutPuts(_T("if"));
1032  /* NOTE: IF cannot have leading redirections */
1033 
1034  if (Cmd->If.Flags & IFFLAG_IGNORECASE)
1035  ConOutPuts(_T(" /I"));
1036 
1037  ConOutChar(_T('\n'));
1038 
1039  SpacePad += 2;
1040 
1041  /*
1042  * Show the IF command condition as a command.
1043  * If it is negated, indent the command more.
1044  */
1045  if (Cmd->If.Flags & IFFLAG_NEGATE)
1046  {
1047  ConOutPrintf(_T("%*s"), SpacePad, _T(""));
1048  ConOutPuts(_T("not\n"));
1049  SpacePad += 2;
1050  }
1051 
1052  ConOutPrintf(_T("%*s"), SpacePad, _T(""));
1053 
1054  /*
1055  * Command name:
1056  * - Unary operator: its name is the command name, and its argument is the command argument.
1057  * - Binary operator: its LHS is the command name, its RHS is the command argument.
1058  *
1059  * Type:
1060  * Windows' CMD (Win2k3 / Win7-10) values are as follows:
1061  * CMDEXTVERSION Type: 0x32 / 0x34
1062  * ERRORLEVEL Type: 0x33 / 0x35
1063  * DEFINED Type: 0x34 / 0x36
1064  * EXIST Type: 0x35 / 0x37
1065  * == Type: 0x37 / 0x39 (String Comparison)
1066  *
1067  * For the following command:
1068  * NOT Type: 0x36 / 0x38
1069  * Windows only prints it without any type / redirection.
1070  *
1071  * For the following command:
1072  * EQU, NEQ, etc. Type: 0x38 / 0x3a (Generic Comparison)
1073  * Windows displays it as command of unknown type.
1074  */
1075 #ifndef MSCMD_ECHO_COMMAND_COMPAT
1076  ConOutPrintf(_T("Cmd: %s Type: %x"),
1077  (Cmd->If.Operator <= IF_MAX_UNARY) ?
1078  IfOperatorString[Cmd->If.Operator] :
1079  Cmd->If.LeftArg,
1080  Cmd->If.Operator);
1081 #else
1082  ConOutPrintf(_T("Cmd: %s Type: %x "),
1083  (Cmd->If.Operator <= IF_MAX_UNARY) ?
1084  IfOperatorString[Cmd->If.Operator] :
1085  Cmd->If.LeftArg,
1086  Cmd->If.Operator);
1087 #endif
1088  /* Arguments */
1089 #ifndef MSCMD_ECHO_COMMAND_COMPAT
1090  ConOutPrintf(_T(" Args: `%s'"), Cmd->If.RightArg);
1091 #else
1092  ConOutPrintf(_T("Args: `%s' "), Cmd->If.RightArg);
1093 #endif
1094 
1095  ConOutChar(_T('\n'));
1096 
1097  if (Cmd->If.Flags & IFFLAG_NEGATE)
1098  {
1099  SpacePad -= 2;
1100  }
1101 
1102  Sub = Cmd->Subcommands;
1103  DumpCommand(Sub, SpacePad);
1104  if (Sub->Next)
1105  {
1106  ConOutPrintf(_T("%*s"), SpacePad - 2, _T(""));
1107  ConOutPuts(_T("else\n"));
1108  DumpCommand(Sub->Next, SpacePad);
1109  }
1110  return;
1111  }
1112 
1113  case C_FOR:
1114  {
1115  ConOutPuts(_T("for"));
1116  /* NOTE: FOR cannot have leading redirections */
1117 
1118  if (Cmd->For.Switches & FOR_DIRS) ConOutPuts(_T(" /D"));
1119  if (Cmd->For.Switches & FOR_F) ConOutPuts(_T(" /F"));
1120  if (Cmd->For.Switches & FOR_LOOP) ConOutPuts(_T(" /L"));
1121  if (Cmd->For.Switches & FOR_RECURSIVE) ConOutPuts(_T(" /R"));
1122  if (Cmd->For.Params)
1123  ConOutPrintf(_T(" %s"), Cmd->For.Params);
1124  ConOutPrintf(_T(" %%%c in (%s) do\n"), Cmd->For.Variable, Cmd->For.List);
1125  /*DumpCommand*/DUMP(Cmd->Subcommands, SpacePad + 2);
1126  return;
1127  }
1128 
1129  default:
1130  ConOutPrintf(_T("*** Unknown type: %x\n"), Cmd->Type);
1131  break;
1132  }
1133 
1134 #undef DUMP
1135 }
1136 
1137 /*
1138  * Reconstruct a parse tree into text form; used for echoing
1139  * batch file commands and FOR instances.
1140  */
1141 VOID
1143 {
1144  PARSED_COMMAND *Sub;
1145  REDIRECTION *Redir;
1146 
1147  switch (Cmd->Type)
1148  {
1149  case C_COMMAND:
1150  {
1151  if (SubstituteForVars(Cmd->Command.First, TempBuf))
1152  ConOutPrintf(_T("%s"), TempBuf);
1153  if (SubstituteForVars(Cmd->Command.Rest, TempBuf))
1154  {
1155  ConOutPrintf(_T("%s"), TempBuf);
1156 #ifdef MSCMD_ECHO_COMMAND_COMPAT
1157  /* NOTE: For Windows compatibility, add a trailing space after printing the command parameter, if present */
1158  if (*TempBuf) ConOutChar(_T(' '));
1159 #endif
1160  }
1161  break;
1162  }
1163 
1164  case C_QUIET:
1165  return;
1166 
1167  case C_BLOCK:
1168  {
1169  BOOLEAN bIsFirstCmdCRLF;
1170 
1171  ConOutChar(_T('('));
1172 
1173  Sub = Cmd->Subcommands;
1174 
1175  bIsFirstCmdCRLF = (Sub && Sub->Next);
1176 
1177 #if defined(MSCMD_ECHO_COMMAND_COMPAT) && defined(MSCMD_PARSER_BUGS)
1178  /*
1179  * We will emulate Windows' CMD handling of "CRLF" and "&" multi-command
1180  * enumeration within parenthesized command blocks.
1181  */
1182  bIsFirstCmdCRLF = bIsFirstCmdCRLF && (Sub->Type != C_MULTI);
1183 #endif
1184 
1185  /*
1186  * Single-command block: display all on one line.
1187  * Multi-command block: display commands on separate lines.
1188  */
1189  if (bIsFirstCmdCRLF)
1190  ConOutChar(_T('\n'));
1191 
1192  for (; Sub; Sub = Sub->Next)
1193  {
1194  EchoCommand(Sub);
1195  if (Sub->Next)
1196 #ifdef MSCMD_ECHO_COMMAND_COMPAT
1197  ConOutPuts(_T(" \n "));
1198 #else
1199  ConOutChar(_T('\n'));
1200 #endif
1201  }
1202 
1203  if (bIsFirstCmdCRLF)
1204  ConOutChar(_T('\n'));
1205 
1206 #ifdef MSCMD_ECHO_COMMAND_COMPAT
1207  /* NOTE: For Windows compatibility, add a trailing space after printing the closing parenthesis */
1208  ConOutPuts(_T(") "));
1209 #else
1210  ConOutChar(_T(')'));
1211 #endif
1212  break;
1213  }
1214 
1215  case C_MULTI:
1216  case C_OR:
1217  case C_AND:
1218  case C_PIPE:
1219  {
1220  Sub = Cmd->Subcommands;
1221  EchoCommand(Sub);
1222  ConOutPrintf(_T(" %s "), OpString[Cmd->Type - C_OP_LOWEST]);
1223  EchoCommand(Sub->Next);
1224  break;
1225  }
1226 
1227  case C_IF:
1228  {
1229  ConOutPuts(_T("if"));
1230  if (Cmd->If.Flags & IFFLAG_IGNORECASE)
1231  ConOutPuts(_T(" /I"));
1232  if (Cmd->If.Flags & IFFLAG_NEGATE)
1233  ConOutPuts(_T(" not"));
1234  if (Cmd->If.LeftArg && SubstituteForVars(Cmd->If.LeftArg, TempBuf))
1235  ConOutPrintf(_T(" %s"), TempBuf);
1236  ConOutPrintf(_T(" %s"), IfOperatorString[Cmd->If.Operator]);
1237  if (SubstituteForVars(Cmd->If.RightArg, TempBuf))
1238  ConOutPrintf(_T(" %s "), TempBuf);
1239  Sub = Cmd->Subcommands;
1240  EchoCommand(Sub);
1241  if (Sub->Next)
1242  {
1243  ConOutPuts(_T(" else "));
1244  EchoCommand(Sub->Next);
1245  }
1246  break;
1247  }
1248 
1249  case C_FOR:
1250  {
1251  ConOutPuts(_T("for"));
1252  if (Cmd->For.Switches & FOR_DIRS) ConOutPuts(_T(" /D"));
1253  if (Cmd->For.Switches & FOR_F) ConOutPuts(_T(" /F"));
1254  if (Cmd->For.Switches & FOR_LOOP) ConOutPuts(_T(" /L"));
1255  if (Cmd->For.Switches & FOR_RECURSIVE) ConOutPuts(_T(" /R"));
1256  if (Cmd->For.Params)
1257  ConOutPrintf(_T(" %s"), Cmd->For.Params);
1258  if (Cmd->For.List && SubstituteForVars(Cmd->For.List, TempBuf))
1259  ConOutPrintf(_T(" %%%c in (%s) do "), Cmd->For.Variable, TempBuf);
1260  else
1261  ConOutPrintf(_T(" %%%c in (%s) do "), Cmd->For.Variable, Cmd->For.List);
1262  EchoCommand(Cmd->Subcommands);
1263  break;
1264  }
1265 
1266  default:
1267  ASSERT(FALSE);
1268  break;
1269  }
1270 
1271  for (Redir = Cmd->Redirections; Redir; Redir = Redir->Next)
1272  {
1273  if (SubstituteForVars(Redir->Filename, TempBuf))
1274  {
1275 #ifdef MSCMD_ECHO_COMMAND_COMPAT
1276  ConOutPrintf(_T("%c%s%s "),
1277  _T('0') + Redir->Number,
1278  RedirString[Redir->Mode], TempBuf);
1279 #else
1280  ConOutPrintf(_T(" %c%s%s"),
1281  _T('0') + Redir->Number,
1282  RedirString[Redir->Mode], TempBuf);
1283 #endif
1284  }
1285  }
1286 }
1287 
1288 /*
1289  * "Unparse" a command into a text form suitable for passing to CMD /C.
1290  * Used for pipes. This is basically the same thing as EchoCommand, but
1291  * writing into a string instead of to standard output.
1292  */
1293 TCHAR *
1295 {
1296  PARSED_COMMAND *Sub;
1297  REDIRECTION *Redir;
1298 
1299 /*
1300  * Since this function has the annoying requirement that it must avoid
1301  * overflowing the supplied buffer, define some helper macros to make
1302  * this less painful.
1303  */
1304 #define CHAR(Char) \
1305 do { \
1306  if (Out == OutEnd) return NULL; \
1307  *Out++ = Char; \
1308 } while (0)
1309 #define STRING(String) \
1310 do { \
1311  if (Out + _tcslen(String) > OutEnd) return NULL; \
1312  Out = _stpcpy(Out, String); \
1313 } while (0)
1314 #define PRINTF(Format, ...) \
1315 do { \
1316  UINT Len = _sntprintf(Out, OutEnd - Out, Format, __VA_ARGS__); \
1317  if (Len > (UINT)(OutEnd - Out)) return NULL; \
1318  Out += Len; \
1319 } while (0)
1320 #define RECURSE(Subcommand) \
1321 do { \
1322  Out = Unparse(Subcommand, Out, OutEnd); \
1323  if (!Out) return NULL; \
1324 } while (0)
1325 
1326  switch (Cmd->Type)
1327  {
1328  case C_COMMAND:
1329  /* This is fragile since there could be special characters, but
1330  * Windows doesn't bother escaping them, so for compatibility
1331  * we probably shouldn't do it either */
1332  if (!SubstituteForVars(Cmd->Command.First, TempBuf)) return NULL;
1333  STRING(TempBuf);
1334  if (!SubstituteForVars(Cmd->Command.Rest, TempBuf)) return NULL;
1335  STRING(TempBuf);
1336  break;
1337 
1338  case C_QUIET:
1339  CHAR(_T('@'));
1340  RECURSE(Cmd->Subcommands);
1341  break;
1342 
1343  case C_BLOCK:
1344  CHAR(_T('('));
1345  for (Sub = Cmd->Subcommands; Sub; Sub = Sub->Next)
1346  {
1347  RECURSE(Sub);
1348  if (Sub->Next)
1349  CHAR(_T('&'));
1350  }
1351  CHAR(_T(')'));
1352  break;
1353 
1354  case C_MULTI:
1355  case C_OR:
1356  case C_AND:
1357  case C_PIPE:
1358  Sub = Cmd->Subcommands;
1359  RECURSE(Sub);
1360  PRINTF(_T(" %s "), OpString[Cmd->Type - C_OP_LOWEST]);
1361  RECURSE(Sub->Next);
1362  break;
1363 
1364  case C_IF:
1365  STRING(_T("if"));
1366  if (Cmd->If.Flags & IFFLAG_IGNORECASE)
1367  STRING(_T(" /I"));
1368  if (Cmd->If.Flags & IFFLAG_NEGATE)
1369  STRING(_T(" not"));
1370  if (Cmd->If.LeftArg && SubstituteForVars(Cmd->If.LeftArg, TempBuf))
1371  PRINTF(_T(" %s"), TempBuf);
1372  PRINTF(_T(" %s"), IfOperatorString[Cmd->If.Operator]);
1373  if (!SubstituteForVars(Cmd->If.RightArg, TempBuf)) return NULL;
1374  PRINTF(_T(" %s "), TempBuf);
1375  Sub = Cmd->Subcommands;
1376  RECURSE(Sub);
1377  if (Sub->Next)
1378  {
1379  STRING(_T(" else "));
1380  RECURSE(Sub->Next);
1381  }
1382  break;
1383 
1384  case C_FOR:
1385  STRING(_T("for"));
1386  if (Cmd->For.Switches & FOR_DIRS) STRING(_T(" /D"));
1387  if (Cmd->For.Switches & FOR_F) STRING(_T(" /F"));
1388  if (Cmd->For.Switches & FOR_LOOP) STRING(_T(" /L"));
1389  if (Cmd->For.Switches & FOR_RECURSIVE) STRING(_T(" /R"));
1390  if (Cmd->For.Params)
1391  PRINTF(_T(" %s"), Cmd->For.Params);
1392  if (Cmd->For.List && SubstituteForVars(Cmd->For.List, TempBuf))
1393  PRINTF(_T(" %%%c in (%s) do "), Cmd->For.Variable, TempBuf);
1394  else
1395  PRINTF(_T(" %%%c in (%s) do "), Cmd->For.Variable, Cmd->For.List);
1396  RECURSE(Cmd->Subcommands);
1397  break;
1398 
1399  default:
1400  ASSERT(FALSE);
1401  break;
1402  }
1403 
1404  for (Redir = Cmd->Redirections; Redir; Redir = Redir->Next)
1405  {
1406  if (!SubstituteForVars(Redir->Filename, TempBuf))
1407  return NULL;
1408  PRINTF(_T(" %c%s%s"), _T('0') + Redir->Number,
1409  RedirString[Redir->Mode], TempBuf);
1410  }
1411  return Out;
1412 
1413 #undef CHAR
1414 #undef STRING
1415 #undef PRINTF
1416 #undef RECURSE
1417 }
1418 
1419 VOID
1421 {
1422  if (Cmd->Subcommands)
1423  FreeCommand(Cmd->Subcommands);
1424  if (Cmd->Next)
1425  FreeCommand(Cmd->Next);
1426  FreeRedirection(Cmd->Redirections);
1427  if (Cmd->Type == C_IF)
1428  {
1429  cmd_free(Cmd->If.LeftArg);
1430  cmd_free(Cmd->If.RightArg);
1431  }
1432  else if (Cmd->Type == C_FOR)
1433  {
1434  cmd_free(Cmd->For.Params);
1435  cmd_free(Cmd->For.List);
1436  }
1437  cmd_free(Cmd);
1438 }
#define FOR_DIRS
Definition: cmd.h:212
static int CurrentTokenType
Definition: parser.c:74
#define IN
Definition: typedefs.h:39
static DECLSPEC_NOINLINE PARSED_COMMAND * ParseCommandPart(REDIRECTION *RedirList)
Definition: parser.c:620
Definition: parser.c:56
struct png_info_def **typedef void(__cdecl typeof(png_destroy_read_struct))(struct png_struct_def **
Definition: typeof.h:49
Type
Definition: Type.h:6
BOOL bIgnoreEcho
Definition: cmd.c:155
LPCSTR PCTSTR
Definition: ntbasedef.h:495
#define RECURSE(Subcommand)
#define _tcsicmp
Definition: xmlstorage.h:205
enum _REDIR_MODE REDIR_MODE
#define IFFLAG_IGNORECASE
Definition: cmd.h:239
static TCHAR CurrentToken[CMDLINE_LENGTH]
Definition: parser.c:73
int _tcscmp(const _TCHAR *s1, const _TCHAR *s2)
Definition: tcscmp.h:8
#define TRUE
Definition: types.h:120
VOID ConOutChar(TCHAR c)
Definition: console.c:123
BYTE Type
Definition: cmd.h:332
#define IF_MAX_UNARY
#define WARN(fmt,...)
Definition: debug.h:112
ush Pos
Definition: deflate.h:92
#define CMDLINE_LENGTH
Definition: help.h:12
static PARSED_COMMAND * ParseBlock(REDIRECTION *RedirList)
Definition: parser.c:329
Definition: cmd.h:326
#define INVALID_HANDLE_VALUE
Definition: compat.h:479
_TCHAR * _tcscpy(_TCHAR *to, const _TCHAR *from)
Definition: tcscpy.h:8
BOOL SubstituteVars(TCHAR *Src, TCHAR *Dest, TCHAR Delim)
Definition: cmd.c:1265
#define ConOutPrintf(szStr,...)
Definition: console.h:42
Definition: shell.h:41
static BOOL IsSeparator(TCHAR Char)
Definition: parser.c:49
VOID FreeCommand(PARSED_COMMAND *Cmd)
Definition: parser.c:1420
static VOID StripQuotes(LPSTR in)
Definition: cmdcons.c:116
#define FOR_F
Definition: cmd.h:213
struct _PARSED_COMMAND * Next
Definition: cmd.h:330
static int ParseToken(TCHAR ExtraEnd, TCHAR *Separators)
Definition: parser.c:132
CHAR * LPTSTR
Definition: xmlstorage.h:192
IN PVCB IN PBCB OUT PDIRENT IN USHORT IN POEM_STRING Filename
Definition: fatprocs.h:934
#define C_OP_LOWEST
Definition: parser.c:19
REDIR_MODE Mode
Definition: cmd.h:390
static BOOL bLineContinuations
Definition: parser.c:68
HANDLE OldHandle
Definition: cmd.h:388
Definition: cmd.h:326
static PARSED_COMMAND * ParseFor(void)
Definition: parser.c:481
static BOOL ParseRedirection(REDIRECTION **List)
Definition: parser.c:257
LPTSTR _stpcpy(LPTSTR, LPCTSTR)
Definition: misc.c:460
#define PRINTF(Format,...)
BOOL bParseError
Definition: parser.c:67
#define FALSE
Definition: types.h:117
unsigned int BOOL
Definition: ntddk_ex.h:94
#define _tcsnicmp
Definition: xmlstorage.h:207
#define FOR_LOOP
Definition: cmd.h:214
size_t __cdecl _tcslen(const _TCHAR *str)
Definition: tcslen.h:9
unsigned char BOOLEAN
smooth NULL
Definition: ftsmooth.c:416
static PARSED_COMMAND * ParseIf(void)
Definition: parser.c:385
Definition: cmd.h:326
_TCHAR * _tcschr(const _TCHAR *s, _XINT c)
Definition: tcschr.h:4
PARSED_COMMAND * ParseCommand(LPTSTR Line)
Definition: parser.c:805
void restart(int argc, const char *argv[])
Definition: cmds.c:2115
BYTE Number
Definition: cmd.h:389
char TCHAR
Definition: xmlstorage.h:189
static PARSED_COMMAND * ParseRem(void)
Definition: parser.c:611
#define _T(x)
Definition: vfdio.h:22
LIST_ENTRY List
Definition: psmgr.c:57
TCHAR ParseLine[CMDLINE_LENGTH]
Definition: parser.c:69
Definition: cmd.h:326
static TCHAR * ParsePos
Definition: parser.c:70
Definition: cmd.h:326
#define IFFLAG_NEGATE
Definition: cmd.h:238
TCHAR Filename[]
Definition: cmd.h:391
DWORD error
Definition: parser.c:105
Definition: cmd.h:251
static const TCHAR *const IfOperatorString[]
Definition: parser.c:25
#define STRING(String)
static TCHAR ParseChar(void)
Definition: parser.c:77
ASSERT((InvokeOnSuccess||InvokeOnError||InvokeOnCancel) ?(CompletionRoutine !=NULL) :TRUE)
BOOL ReadLine(TCHAR *commandline, BOOL bMore)
Definition: cmd.c:1502
VOID ParseErrorEx(IN PCTSTR s)
Definition: parser.c:115
Definition: ncftp.h:79
#define FOR_RECURSIVE
Definition: cmd.h:215
BOOL bEnableExtensions
Definition: cmd.c:161
static const TCHAR RedirString[][3]
Definition: parser.c:23
Definition: sacdrv.h:277
Definition: cmd.h:326
#define memcpy(s1, s2, n)
Definition: mkisofs.h:878
static PARSED_COMMAND * ParseCommandOp(int OpType)
Definition: parser.c:755
GLdouble s
Definition: gl.h:2039
#define STANDARD_SEPS
Definition: cmd.h:324
uint32_t DWORD_PTR
Definition: typedefs.h:65
TCHAR * Unparse(PARSED_COMMAND *Cmd, TCHAR *Out, TCHAR *OutEnd)
Definition: parser.c:1294
_In_opt_ PENTER_STATE_SYSTEM_HANDLER _In_opt_ PVOID _In_ LONG _In_opt_ LONG volatile * Number
Definition: ntpoapi.h:204
unsigned char BYTE
Definition: xxhash.c:193
#define DUMP(Command, Pad)
BOOL SubstituteForVars(TCHAR *Src, TCHAR *Dest)
Definition: cmd.c:1441
#define cmd_alloc(size)
Definition: cmddbg.h:29
static TCHAR CurChar
Definition: parser.c:71
#define cmd_free(ptr)
Definition: cmddbg.h:31
Definition: cmd.h:326
void(* Func)(int)
VOID DumpCommand(PARSED_COMMAND *Cmd, ULONG SpacePad)
Definition: parser.c:873
BOOLEAN fDumpTokens
Definition: parser.c:16
#define cmd_dup(str)
Definition: cmddbg.h:32
#define FIELD_OFFSET(t, f)
Definition: typedefs.h:255
static int InsideBlock
Definition: parser.c:75
static const TCHAR OpString[][3]
Definition: parser.c:21
VOID FreeRedirection(REDIRECTION *)
Definition: redir.c:153
struct _REDIRECTION * Next
Definition: cmd.h:387
#define _istspace
Definition: tchar.h:1504
unsigned int ULONG
Definition: retypes.h:1
static VOID DumpRedir(REDIRECTION *Redirections)
Definition: parser.c:855
#define IF_MAX_COMPARISON
VOID error_syntax(PCTSTR s)
Definition: error.c:152
#define C_OP_HIGHEST
Definition: parser.c:20
Definition: cmd.h:326
static TCHAR TempBuf[CMDLINE_LENGTH]
Definition: parser.c:65
Definition: cmd.h:326
#define memset(x, y, z)
Definition: compat.h:39
#define CHAR(Char)
#define DECLSPEC_NOINLINE
Definition: ntbasedef.h:231
static void dump(const void *ptr, unsigned len)
Definition: msc.c:95
BOOLEAN fDumpParse
Definition: parser.c:17
static __inline VOID ParseError(VOID)
Definition: parser.c:123
VOID EchoCommand(PARSED_COMMAND *Cmd)
Definition: parser.c:1142
static PARSED_COMMAND * ParsePrimary(void)
Definition: parser.c:692
VOID ConOutPuts(LPTSTR szText)
Definition: tee.c:27