ReactOS  0.4.15-dev-439-g292f67a
parser.c
Go to the documentation of this file.
1 /*
2  * PARSER.C - command parsing.
3  */
4 
5 #include "precomp.h"
6 
7 #define C_OP_LOWEST C_MULTI
8 #define C_OP_HIGHEST C_PIPE
9 static const TCHAR OpString[][3] = { _T("&"), _T("||"), _T("&&"), _T("|") };
10 
11 static const TCHAR RedirString[][3] = { _T("<"), _T(">"), _T(">>") };
12 
13 static const TCHAR *const IfOperatorString[] =
14 {
15  _T("cmdextversion"),
16  _T("defined"),
17  _T("errorlevel"),
18  _T("exist"),
19 #define IF_MAX_UNARY IF_EXIST
20  _T("=="),
21  _T("equ"),
22  _T("gtr"),
23  _T("geq"),
24  _T("lss"),
25  _T("leq"),
26  _T("neq"),
27 #define IF_MAX_COMPARISON IF_NEQ
28 };
29 
30 /* These three characters act like spaces to the parser in most contexts */
31 #define STANDARD_SEPS _T(",;=")
32 
33 static BOOL IsSeparator(TCHAR Char)
34 {
35  return _istspace(Char) || (Char && _tcschr(STANDARD_SEPS, Char));
36 }
37 
38 enum
39 {
46 };
47 
51 static TCHAR *ParsePos;
52 static TCHAR CurChar;
53 
55 static int CurrentTokenType;
56 static int InsideBlock;
57 
58 static TCHAR ParseChar(void)
59 {
60  TCHAR Char;
61 
62  if (bParseError)
63  return (CurChar = 0);
64 
65 restart:
66  /*
67  * Although CRs can be injected into a line via an environment
68  * variable substitution, the parser ignores them - they won't
69  * even separate tokens.
70  */
71  do
72  {
73  Char = *ParsePos++;
74  }
75  while (Char == _T('\r'));
76 
77  if (!Char)
78  {
79  ParsePos--;
81  {
82  if (!ReadLine(ParseLine, TRUE))
83  {
84  /* ^C pressed, or line was too long */
85  bParseError = TRUE;
86  }
87  else if (*(ParsePos = ParseLine))
88  {
89  goto restart;
90  }
91  }
92  }
93  return (CurChar = Char);
94 }
95 
96 static void ParseError(void)
97 {
99  bParseError = TRUE;
100 }
101 
102 /*
103  * Yes, cmd has a Lexical Analyzer. Whenever the parser gives an "xxx was
104  * unexpected at this time." message, it shows what the last token read was.
105  */
106 static int ParseToken(TCHAR ExtraEnd, TCHAR *Separators)
107 {
109  TCHAR Char;
110  int Type;
111  BOOL bInQuote = FALSE;
112 
113  for (Char = CurChar; Char && Char != _T('\n'); Char = ParseChar())
114  {
115  bInQuote ^= (Char == _T('"'));
116  if (!bInQuote)
117  {
118  if (Separators != NULL)
119  {
120  if (_istspace(Char) || _tcschr(Separators, Char))
121  {
122  /* Skip leading separators */
123  if (Out == CurrentToken)
124  continue;
125  break;
126  }
127  }
128 
129  /* Check for numbered redirection */
130  if ((Char >= _T('0') && Char <= _T('9') &&
131  (ParsePos == &ParseLine[1] || IsSeparator(ParsePos[-2]))
132  && (*ParsePos == _T('<') || *ParsePos == _T('>'))))
133  {
134  break;
135  }
136 
137  if (Char == ExtraEnd)
138  break;
139  if (InsideBlock && Char == _T(')'))
140  break;
141  if (_tcschr(_T("&|<>"), Char))
142  break;
143 
144  if (Char == _T('^'))
145  {
146  Char = ParseChar();
147  /* Eat up a \n, allowing line continuation */
148  if (Char == _T('\n'))
149  Char = ParseChar();
150  /* Next character is a forced literal */
151  }
152  }
153  if (Out == &CurrentToken[CMDLINE_LENGTH - 1])
154  break;
155  *Out++ = Char;
156  }
157 
158  /* Check if we got at least one character before reaching a special one.
159  * If so, return them and leave the special for the next call. */
160  if (Out != CurrentToken)
161  {
162  Type = TOK_NORMAL;
163  }
164  else if (Char == _T('('))
165  {
167  *Out++ = Char;
168  ParseChar();
169  }
170  else if (Char == _T(')'))
171  {
173  *Out++ = Char;
174  ParseChar();
175  }
176  else if (Char == _T('&') || Char == _T('|'))
177  {
178  Type = TOK_OPERATOR;
179  *Out++ = Char;
180  Char = ParseChar();
181  /* check for && or || */
182  if (Char == Out[-1])
183  {
184  *Out++ = Char;
185  ParseChar();
186  }
187  }
188  else if ((Char >= _T('0') && Char <= _T('9'))
189  || (Char == _T('<') || Char == _T('>')))
190  {
192  if (Char >= _T('0') && Char <= _T('9'))
193  {
194  *Out++ = Char;
195  Char = ParseChar();
196  }
197  *Out++ = Char;
198  Char = ParseChar();
199  if (Char == Out[-1])
200  {
201  /* Strangely, the tokenizer allows << as well as >>... (it
202  * will cause an error when trying to parse it though) */
203  *Out++ = Char;
204  Char = ParseChar();
205  }
206  if (Char == _T('&'))
207  {
208  *Out++ = Char;
209  while (IsSeparator(Char = ParseChar()))
210  ;
211  if (Char >= _T('0') && Char <= _T('9'))
212  {
213  *Out++ = Char;
214  ParseChar();
215  }
216  }
217  }
218  else
219  {
220  Type = TOK_END;
221  }
222  *Out = _T('\0');
223  return CurrentTokenType = Type;
224 }
225 
227 {
228  TCHAR *Tok = CurrentToken;
229  BYTE Number;
230  REDIR_MODE RedirMode;
231  REDIRECTION *Redir;
232 
233  if (*Tok >= _T('0') && *Tok <= _T('9'))
234  Number = *Tok++ - _T('0');
235  else
236  Number = *Tok == _T('<') ? 0 : 1;
237 
238  if (*Tok++ == _T('<'))
239  {
240  RedirMode = REDIR_READ;
241  if (*Tok == _T('<'))
242  goto fail;
243  }
244  else
245  {
246  RedirMode = REDIR_WRITE;
247  if (*Tok == _T('>'))
248  {
249  RedirMode = REDIR_APPEND;
250  Tok++;
251  }
252  }
253 
254  if (!*Tok)
255  {
256  /* The file name was not part of this token, so it'll be the next one */
258  goto fail;
259  Tok = CurrentToken;
260  }
261 
262  /* If a redirection for this handle number already exists, delete it */
263  while ((Redir = *List))
264  {
265  if (Redir->Number == Number)
266  {
267  *List = Redir->Next;
268  cmd_free(Redir);
269  continue;
270  }
271  List = &Redir->Next;
272  }
273 
274  Redir = cmd_alloc(FIELD_OFFSET(REDIRECTION, Filename[_tcslen(Tok) + 1]));
275  if (!Redir)
276  {
277  WARN("Cannot allocate memory for Redir!\n");
278  goto fail;
279  }
280  Redir->Next = NULL;
282  Redir->Number = Number;
283  Redir->Mode = RedirMode;
284  _tcscpy(Redir->Filename, Tok);
285  *List = Redir;
286  return TRUE;
287 
288 fail:
289  ParseError();
291  *List = NULL;
292  return FALSE;
293 }
294 
295 static PARSED_COMMAND *ParseCommandOp(int OpType);
296 
297 /* Parse a parenthesized block */
299 {
300  PARSED_COMMAND *Cmd, *Sub, **NextPtr;
301 
302  Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
303  if (!Cmd)
304  {
305  WARN("Cannot allocate memory for Cmd!\n");
306  ParseError();
307  FreeRedirection(RedirList);
308  return NULL;
309  }
310  Cmd->Type = C_BLOCK;
311  Cmd->Next = NULL;
312  Cmd->Subcommands = NULL;
313  Cmd->Redirections = RedirList;
314 
315  /* Read the block contents */
316  NextPtr = &Cmd->Subcommands;
317  InsideBlock++;
318  while (1)
319  {
321  if (Sub)
322  {
323  *NextPtr = Sub;
324  NextPtr = &Sub->Next;
325  }
326  else if (bParseError)
327  {
328  InsideBlock--;
329  FreeCommand(Cmd);
330  return NULL;
331  }
332 
334  break;
335 
336  /* Skip past the \n */
337  ParseChar();
338  }
339  InsideBlock--;
340 
341  /* Process any trailing redirections */
343  {
344  if (!ParseRedirection(&Cmd->Redirections))
345  {
346  FreeCommand(Cmd);
347  return NULL;
348  }
349  }
350  return Cmd;
351 }
352 
353 /* Parse an IF statement */
354 static PARSED_COMMAND *ParseIf(void)
355 {
356  int Type;
358 
359  Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
360  if (!Cmd)
361  {
362  WARN("Cannot allocate memory for Cmd!\n");
363  ParseError();
364  return NULL;
365  }
366  memset(Cmd, 0, sizeof(PARSED_COMMAND));
367  Cmd->Type = C_IF;
368 
370  if (_tcsicmp(CurrentToken, _T("/I")) == 0)
371  {
372  Cmd->If.Flags |= IFFLAG_IGNORECASE;
374  }
375  if (_tcsicmp(CurrentToken, _T("not")) == 0)
376  {
377  Cmd->If.Flags |= IFFLAG_NEGATE;
379  }
380 
381  if (Type != TOK_NORMAL)
382  {
383  FreeCommand(Cmd);
384  ParseError();
385  return NULL;
386  }
387 
388  /* Check for unary operators */
389  for (; Cmd->If.Operator <= IF_MAX_UNARY; Cmd->If.Operator++)
390  {
391  if (_tcsicmp(CurrentToken, IfOperatorString[Cmd->If.Operator]) == 0)
392  {
394  {
395  FreeCommand(Cmd);
396  ParseError();
397  return NULL;
398  }
399  Cmd->If.RightArg = cmd_dup(CurrentToken);
400  goto condition_done;
401  }
402  }
403 
404  /* It must be a two-argument (comparison) operator. It could be ==, so
405  * the equals sign can't be treated as whitespace here. */
406  Cmd->If.LeftArg = cmd_dup(CurrentToken);
407  ParseToken(0, _T(",;"));
408 
409  /* The right argument can come immediately after == */
410  if (_tcsnicmp(CurrentToken, _T("=="), 2) == 0 && CurrentToken[2])
411  {
412  Cmd->If.RightArg = cmd_dup(&CurrentToken[2]);
413  goto condition_done;
414  }
415 
416  for (; Cmd->If.Operator <= IF_MAX_COMPARISON; Cmd->If.Operator++)
417  {
418  if (_tcsicmp(CurrentToken, IfOperatorString[Cmd->If.Operator]) == 0)
419  {
421  break;
422  Cmd->If.RightArg = cmd_dup(CurrentToken);
423  goto condition_done;
424  }
425  }
426  FreeCommand(Cmd);
427  ParseError();
428  return NULL;
429 
430 condition_done:
431  Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST);
432  if (Cmd->Subcommands == NULL)
433  {
434  FreeCommand(Cmd);
435  return NULL;
436  }
437  if (_tcsicmp(CurrentToken, _T("else")) == 0)
438  {
439  Cmd->Subcommands->Next = ParseCommandOp(C_OP_LOWEST);
440  if (Cmd->Subcommands->Next == NULL)
441  {
442  FreeCommand(Cmd);
443  return NULL;
444  }
445  }
446 
447  return Cmd;
448 }
449 
450 /*
451  * Parse a FOR command.
452  * Syntax is: FOR [options] %var IN (list) DO command
453  */
455 {
458  TCHAR *Pos = List;
459 
460  Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
461  if (!Cmd)
462  {
463  WARN("Cannot allocate memory for Cmd!\n");
464  ParseError();
465  return NULL;
466  }
467  memset(Cmd, 0, sizeof(PARSED_COMMAND));
468  Cmd->Type = C_FOR;
469 
470  while (1)
471  {
472  if (_tcsicmp(CurrentToken, _T("/D")) == 0)
473  Cmd->For.Switches |= FOR_DIRS;
474  else if (_tcsicmp(CurrentToken, _T("/F")) == 0)
475  {
476  Cmd->For.Switches |= FOR_F;
477  if (!Cmd->For.Params)
478  {
480  if (CurrentToken[0] == _T('/') || CurrentToken[0] == _T('%'))
481  break;
482  Cmd->For.Params = cmd_dup(CurrentToken);
483  }
484  }
485  else if (_tcsicmp(CurrentToken, _T("/L")) == 0)
486  Cmd->For.Switches |= FOR_LOOP;
487  else if (_tcsicmp(CurrentToken, _T("/R")) == 0)
488  {
489  Cmd->For.Switches |= FOR_RECURSIVE;
490  if (!Cmd->For.Params)
491  {
493  if (CurrentToken[0] == _T('/') || CurrentToken[0] == _T('%'))
494  break;
496  Cmd->For.Params = cmd_dup(CurrentToken);
497  }
498  }
499  else
500  break;
502  }
503 
504  /* Make sure there aren't two different switches specified
505  * at the same time, unless they're /D and /R */
506  if ((Cmd->For.Switches & (Cmd->For.Switches - 1)) != 0
507  && Cmd->For.Switches != (FOR_DIRS | FOR_RECURSIVE))
508  {
509  goto error;
510  }
511 
512  /* Variable name should be % and just one other character */
513  if (CurrentToken[0] != _T('%') || _tcslen(CurrentToken) != 2)
514  goto error;
515  Cmd->For.Variable = CurrentToken[1];
516 
518  if (_tcsicmp(CurrentToken, _T("in")) != 0)
519  goto error;
520 
522  goto error;
523 
524  while (1)
525  {
526  int Type;
527 
528  /* Pretend we're inside a block so the tokenizer will stop on ')' */
529  InsideBlock++;
531  InsideBlock--;
532 
533  if (Type == TOK_END_BLOCK)
534  break;
535 
536  if (Type == TOK_END)
537  {
538  /* Skip past the \n */
539  ParseChar();
540  continue;
541  }
542 
543  if (Type != TOK_NORMAL)
544  goto error;
545 
546  if (Pos != List)
547  *Pos++ = _T(' ');
548 
550  goto error;
552  }
553  *Pos = _T('\0');
554  Cmd->For.List = cmd_dup(List);
555 
557  if (_tcsicmp(CurrentToken, _T("do")) != 0)
558  goto error;
559 
560  Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST);
561  if (Cmd->Subcommands == NULL)
562  {
563  FreeCommand(Cmd);
564  return NULL;
565  }
566 
567  return Cmd;
568 
569 error:
570  FreeCommand(Cmd);
571  ParseError();
572  return NULL;
573 }
574 
575 /* Parse a REM command */
577 {
578  /* "Ignore" the rest of the line.
579  * (Line continuations will still be parsed, though.) */
580  while (ParseToken(0, NULL) != TOK_END)
581  ;
582  return NULL;
583 }
584 
586 {
587  TCHAR ParsedLine[CMDLINE_LENGTH];
589  PARSED_COMMAND *(*Func)(void);
590 
591  TCHAR *Pos = _stpcpy(ParsedLine, CurrentToken) + 1;
592  DWORD_PTR TailOffset = Pos - ParsedLine;
593 
594  /* Check for special forms */
595  if ((Func = ParseFor, _tcsicmp(ParsedLine, _T("for")) == 0) ||
596  (Func = ParseIf, _tcsicmp(ParsedLine, _T("if")) == 0) ||
597  (Func = ParseRem, _tcsicmp(ParsedLine, _T("rem")) == 0))
598  {
600  /* Do special parsing only if it's not followed by /? */
601  if (_tcscmp(CurrentToken, _T("/?")) != 0)
602  {
603  if (RedirList)
604  {
605  ParseError();
606  FreeRedirection(RedirList);
607  return NULL;
608  }
609  return Func();
610  }
611  Pos = _stpcpy(Pos, _T(" /?"));
612  }
613 
614  /* Now get the tail */
615  while (1)
616  {
617  int Type = ParseToken(0, NULL);
618  if (Type == TOK_NORMAL)
619  {
620  if (Pos + _tcslen(CurrentToken) >= &ParsedLine[CMDLINE_LENGTH])
621  {
622  ParseError();
623  FreeRedirection(RedirList);
624  return NULL;
625  }
627  }
628  else if (Type == TOK_REDIRECTION)
629  {
630  if (!ParseRedirection(&RedirList))
631  return NULL;
632  }
633  else
634  {
635  break;
636  }
637  }
638  *Pos++ = _T('\0');
639 
640  Cmd = cmd_alloc(FIELD_OFFSET(PARSED_COMMAND, Command.First[Pos - ParsedLine]));
641  if (!Cmd)
642  {
643  WARN("Cannot allocate memory for Cmd!\n");
644  ParseError();
645  FreeRedirection(RedirList);
646  return NULL;
647  }
648  Cmd->Type = C_COMMAND;
649  Cmd->Next = NULL;
650  Cmd->Subcommands = NULL;
651  Cmd->Redirections = RedirList;
652  memcpy(Cmd->Command.First, ParsedLine, (Pos - ParsedLine) * sizeof(TCHAR));
653  Cmd->Command.Rest = Cmd->Command.First + TailOffset;
654  return Cmd;
655 }
656 
658 {
659  REDIRECTION *RedirList = NULL;
660  int Type;
661 
662  while (IsSeparator(CurChar))
663  {
664  if (CurChar == _T('\n'))
665  return NULL;
666  ParseChar();
667  }
668 
669  if (!CurChar)
670  return NULL;
671 
672  if (CurChar == _T(':'))
673  {
674  /* "Ignore" the rest of the line.
675  * (Line continuations will still be parsed, though.) */
676  while (ParseToken(0, NULL) != TOK_END)
677  ;
678  return NULL;
679  }
680 
681  if (CurChar == _T('@'))
682  {
684  ParseChar();
685  Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
686  if (!Cmd)
687  {
688  WARN("Cannot allocate memory for Cmd!\n");
689  ParseError();
690  return NULL;
691  }
692  Cmd->Type = C_QUIET;
693  Cmd->Next = NULL;
694  /* @ acts like a unary operator with low precedence,
695  * so call the top-level parser */
696  Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST);
697  Cmd->Redirections = NULL;
698  return Cmd;
699  }
700 
701  /* Process leading redirections and get the head of the command */
702  while ((Type = ParseToken(_T('('), STANDARD_SEPS)) == TOK_REDIRECTION)
703  {
704  if (!ParseRedirection(&RedirList))
705  return NULL;
706  }
707 
708  if (Type == TOK_NORMAL)
709  return ParseCommandPart(RedirList);
710  else if (Type == TOK_BEGIN_BLOCK)
711  return ParseBlock(RedirList);
712  else if (Type == TOK_END_BLOCK && !RedirList)
713  return NULL;
714 
715  ParseError();
716  FreeRedirection(RedirList);
717  return NULL;
718 }
719 
720 static PARSED_COMMAND *ParseCommandOp(int OpType)
721 {
722  PARSED_COMMAND *Cmd, *Left, *Right;
723 
724  if (OpType == C_OP_HIGHEST)
725  Cmd = ParsePrimary();
726  else
727  Cmd = ParseCommandOp(OpType + 1);
728 
729  if (Cmd && !_tcscmp(CurrentToken, OpString[OpType - C_OP_LOWEST]))
730  {
731  Left = Cmd;
732  Right = ParseCommandOp(OpType);
733  if (!Right)
734  {
735  if (!bParseError)
736  {
737  /* & is allowed to have an empty RHS */
738  if (OpType == C_MULTI)
739  return Left;
740  ParseError();
741  }
742  FreeCommand(Left);
743  return NULL;
744  }
745 
746  Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
747  if (!Cmd)
748  {
749  WARN("Cannot allocate memory for Cmd!\n");
750  ParseError();
751  FreeCommand(Left);
752  FreeCommand(Right);
753  return NULL;
754  }
755  Cmd->Type = OpType;
756  Cmd->Next = NULL;
757  Cmd->Redirections = NULL;
758  Cmd->Subcommands = Left;
759  Left->Next = Right;
760  Right->Next = NULL;
761  }
762 
763  return Cmd;
764 }
765 
768 {
770 
771  if (Line)
772  {
773  if (!SubstituteVars(Line, ParseLine, _T('%')))
774  return NULL;
776  }
777  else
778  {
779  if (!ReadLine(ParseLine, FALSE))
780  return NULL;
782  }
783  bParseError = FALSE;
785  CurChar = _T(' ');
786 
788  if (Cmd)
789  {
790  if (CurrentTokenType != TOK_END)
791  ParseError();
792  if (bParseError)
793  {
794  FreeCommand(Cmd);
795  Cmd = NULL;
796  }
797  bIgnoreEcho = FALSE;
798  }
799  else
800  {
801  bIgnoreEcho = TRUE;
802  }
803  return Cmd;
804 }
805 
806 
807 /*
808  * Reconstruct a parse tree into text form; used for echoing
809  * batch file commands and FOR instances.
810  */
811 VOID
813 {
814  TCHAR Buf[CMDLINE_LENGTH];
815  PARSED_COMMAND *Sub;
816  REDIRECTION *Redir;
817 
818  switch (Cmd->Type)
819  {
820  case C_COMMAND:
821  if (SubstituteForVars(Cmd->Command.First, Buf))
822  ConOutPrintf(_T("%s"), Buf);
823  if (SubstituteForVars(Cmd->Command.Rest, Buf))
824  ConOutPrintf(_T("%s"), Buf);
825  break;
826  case C_QUIET:
827  return;
828  case C_BLOCK:
829  ConOutChar(_T('('));
830  Sub = Cmd->Subcommands;
831  if (Sub && !Sub->Next)
832  {
833  /* Single-command block: display all on one line */
834  EchoCommand(Sub);
835  }
836  else if (Sub)
837  {
838  /* Multi-command block: display parenthesis on separate lines */
839  ConOutChar(_T('\n'));
840  do
841  {
842  EchoCommand(Sub);
843  ConOutChar(_T('\n'));
844  Sub = Sub->Next;
845  } while (Sub);
846  }
847  ConOutChar(_T(')'));
848  break;
849  case C_MULTI:
850  case C_IFFAILURE:
851  case C_IFSUCCESS:
852  case C_PIPE:
853  Sub = Cmd->Subcommands;
854  EchoCommand(Sub);
855  ConOutPrintf(_T(" %s "), OpString[Cmd->Type - C_OP_LOWEST]);
856  EchoCommand(Sub->Next);
857  break;
858  case C_IF:
859  ConOutPrintf(_T("if"));
860  if (Cmd->If.Flags & IFFLAG_IGNORECASE)
861  ConOutPrintf(_T(" /I"));
862  if (Cmd->If.Flags & IFFLAG_NEGATE)
863  ConOutPrintf(_T(" not"));
864  if (Cmd->If.LeftArg && SubstituteForVars(Cmd->If.LeftArg, Buf))
865  ConOutPrintf(_T(" %s"), Buf);
866  ConOutPrintf(_T(" %s"), IfOperatorString[Cmd->If.Operator]);
867  if (SubstituteForVars(Cmd->If.RightArg, Buf))
868  ConOutPrintf(_T(" %s "), Buf);
869  Sub = Cmd->Subcommands;
870  EchoCommand(Sub);
871  if (Sub->Next)
872  {
873  ConOutPrintf(_T(" else "));
874  EchoCommand(Sub->Next);
875  }
876  break;
877  case C_FOR:
878  ConOutPrintf(_T("for"));
879  if (Cmd->For.Switches & FOR_DIRS) ConOutPrintf(_T(" /D"));
880  if (Cmd->For.Switches & FOR_F) ConOutPrintf(_T(" /F"));
881  if (Cmd->For.Switches & FOR_LOOP) ConOutPrintf(_T(" /L"));
882  if (Cmd->For.Switches & FOR_RECURSIVE) ConOutPrintf(_T(" /R"));
883  if (Cmd->For.Params)
884  ConOutPrintf(_T(" %s"), Cmd->For.Params);
885  ConOutPrintf(_T(" %%%c in (%s) do "), Cmd->For.Variable, Cmd->For.List);
886  EchoCommand(Cmd->Subcommands);
887  break;
888  }
889 
890  for (Redir = Cmd->Redirections; Redir; Redir = Redir->Next)
891  {
892  if (SubstituteForVars(Redir->Filename, Buf))
893  {
894  ConOutPrintf(_T(" %c%s%s"), _T('0') + Redir->Number,
895  RedirString[Redir->Mode], Buf);
896  }
897  }
898 }
899 
900 /*
901  * "Unparse" a command into a text form suitable for passing to CMD /C.
902  * Used for pipes. This is basically the same thing as EchoCommand, but
903  * writing into a string instead of to standard output.
904  */
905 TCHAR *
907 {
908  TCHAR Buf[CMDLINE_LENGTH];
909  PARSED_COMMAND *Sub;
910  REDIRECTION *Redir;
911 
912 /*
913  * Since this function has the annoying requirement that it must avoid
914  * overflowing the supplied buffer, define some helper macros to make
915  * this less painful.
916  */
917 #define CHAR(Char) \
918 do { \
919  if (Out == OutEnd) return NULL; \
920  *Out++ = Char; \
921 } while (0)
922 #define STRING(String) \
923 do { \
924  if (Out + _tcslen(String) > OutEnd) return NULL; \
925  Out = _stpcpy(Out, String); \
926 } while (0)
927 #define PRINTF(Format, ...) \
928 do { \
929  UINT Len = _sntprintf(Out, OutEnd - Out, Format, __VA_ARGS__); \
930  if (Len > (UINT)(OutEnd - Out)) return NULL; \
931  Out += Len; \
932 } while (0)
933 #define RECURSE(Subcommand) \
934 do { \
935  Out = Unparse(Subcommand, Out, OutEnd); \
936  if (!Out) return NULL; \
937 } while (0)
938 
939  switch (Cmd->Type)
940  {
941  case C_COMMAND:
942  /* This is fragile since there could be special characters, but
943  * Windows doesn't bother escaping them, so for compatibility
944  * we probably shouldn't do it either */
945  if (!SubstituteForVars(Cmd->Command.First, Buf)) return NULL;
946  STRING(Buf);
947  if (!SubstituteForVars(Cmd->Command.Rest, Buf)) return NULL;
948  STRING(Buf);
949  break;
950  case C_QUIET:
951  CHAR(_T('@'));
952  RECURSE(Cmd->Subcommands);
953  break;
954  case C_BLOCK:
955  CHAR(_T('('));
956  for (Sub = Cmd->Subcommands; Sub; Sub = Sub->Next)
957  {
958  RECURSE(Sub);
959  if (Sub->Next)
960  CHAR(_T('&'));
961  }
962  CHAR(_T(')'));
963  break;
964  case C_MULTI:
965  case C_IFFAILURE:
966  case C_IFSUCCESS:
967  case C_PIPE:
968  Sub = Cmd->Subcommands;
969  RECURSE(Sub);
970  PRINTF(_T(" %s "), OpString[Cmd->Type - C_OP_LOWEST]);
971  RECURSE(Sub->Next);
972  break;
973  case C_IF:
974  STRING(_T("if"));
975  if (Cmd->If.Flags & IFFLAG_IGNORECASE)
976  STRING(_T(" /I"));
977  if (Cmd->If.Flags & IFFLAG_NEGATE)
978  STRING(_T(" not"));
979  if (Cmd->If.LeftArg && SubstituteForVars(Cmd->If.LeftArg, Buf))
980  PRINTF(_T(" %s"), Buf);
981  PRINTF(_T(" %s"), IfOperatorString[Cmd->If.Operator]);
982  if (!SubstituteForVars(Cmd->If.RightArg, Buf)) return NULL;
983  PRINTF(_T(" %s "), Buf);
984  Sub = Cmd->Subcommands;
985  RECURSE(Sub);
986  if (Sub->Next)
987  {
988  STRING(_T(" else "));
989  RECURSE(Sub->Next);
990  }
991  break;
992  case C_FOR:
993  STRING(_T("for"));
994  if (Cmd->For.Switches & FOR_DIRS) STRING(_T(" /D"));
995  if (Cmd->For.Switches & FOR_F) STRING(_T(" /F"));
996  if (Cmd->For.Switches & FOR_LOOP) STRING(_T(" /L"));
997  if (Cmd->For.Switches & FOR_RECURSIVE) STRING(_T(" /R"));
998  if (Cmd->For.Params)
999  PRINTF(_T(" %s"), Cmd->For.Params);
1000  PRINTF(_T(" %%%c in (%s) do "), Cmd->For.Variable, Cmd->For.List);
1001  RECURSE(Cmd->Subcommands);
1002  break;
1003  }
1004 
1005  for (Redir = Cmd->Redirections; Redir; Redir = Redir->Next)
1006  {
1007  if (!SubstituteForVars(Redir->Filename, Buf))
1008  return NULL;
1009  PRINTF(_T(" %c%s%s"), _T('0') + Redir->Number,
1010  RedirString[Redir->Mode], Buf);
1011  }
1012  return Out;
1013 }
1014 
1015 VOID
1017 {
1018  if (Cmd->Subcommands)
1019  FreeCommand(Cmd->Subcommands);
1020  if (Cmd->Next)
1021  FreeCommand(Cmd->Next);
1022  FreeRedirection(Cmd->Redirections);
1023  if (Cmd->Type == C_IF)
1024  {
1025  cmd_free(Cmd->If.LeftArg);
1026  cmd_free(Cmd->If.RightArg);
1027  }
1028  else if (Cmd->Type == C_FOR)
1029  {
1030  cmd_free(Cmd->For.Params);
1031  cmd_free(Cmd->For.List);
1032  }
1033  cmd_free(Cmd);
1034 }
#define FOR_DIRS
Definition: cmd.h:209
static int CurrentTokenType
Definition: parser.c:55
static DECLSPEC_NOINLINE PARSED_COMMAND * ParseCommandPart(REDIRECTION *RedirList)
Definition: parser.c:585
Definition: cmd.h:304
Definition: cmd.h:304
#define TRUE
Definition: types.h:120
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
#define RECURSE(Subcommand)
Definition: cmd.h:304
#define _tcsicmp
Definition: xmlstorage.h:205
enum _REDIR_MODE REDIR_MODE
#define IFFLAG_IGNORECASE
Definition: cmd.h:236
static TCHAR CurrentToken[CMDLINE_LENGTH]
Definition: parser.c:54
int _tcscmp(const _TCHAR *s1, const _TCHAR *s2)
Definition: tcscmp.h:8
VOID ConOutChar(TCHAR c)
Definition: console.c:123
#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:298
#define INVALID_HANDLE_VALUE
Definition: compat.h:400
_TCHAR * _tcscpy(_TCHAR *to, const _TCHAR *from)
Definition: tcscpy.h:8
Definition: parser.c:40
BOOL SubstituteVars(TCHAR *Src, TCHAR *Dest, TCHAR Delim)
Definition: cmd.c:1225
Definition: cmd.h:304
#define ConOutPrintf(szStr,...)
Definition: console.h:42
Definition: shell.h:41
static BOOL IsSeparator(TCHAR Char)
Definition: parser.c:33
VOID FreeCommand(PARSED_COMMAND *Cmd)
Definition: parser.c:1016
static VOID StripQuotes(LPSTR in)
Definition: cmdcons.c:116
#define FOR_F
Definition: cmd.h:210
struct _PARSED_COMMAND * Next
Definition: cmd.h:308
static int ParseToken(TCHAR ExtraEnd, TCHAR *Separators)
Definition: parser.c:106
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:7
REDIR_MODE Mode
Definition: cmd.h:363
static BOOL bLineContinuations
Definition: parser.c:49
HANDLE OldHandle
Definition: cmd.h:361
static PARSED_COMMAND * ParseFor(void)
Definition: parser.c:454
static BOOL ParseRedirection(REDIRECTION **List)
Definition: parser.c:226
LPTSTR _stpcpy(LPTSTR, LPCTSTR)
Definition: misc.c:460
#define PRINTF(Format,...)
static BOOL bParseError
Definition: parser.c:48
unsigned int BOOL
Definition: ntddk_ex.h:94
#define _tcsnicmp
Definition: xmlstorage.h:207
#define FOR_LOOP
Definition: cmd.h:211
size_t __cdecl _tcslen(const _TCHAR *str)
Definition: tcslen.h:9
smooth NULL
Definition: ftsmooth.c:416
static PARSED_COMMAND * ParseIf(void)
Definition: parser.c:354
_TCHAR * _tcschr(const _TCHAR *s, _XINT c)
Definition: tcschr.h:4
PARSED_COMMAND * ParseCommand(LPTSTR Line)
Definition: parser.c:767
void restart(int argc, const char *argv[])
Definition: cmds.c:2115
BYTE Number
Definition: cmd.h:362
char TCHAR
Definition: xmlstorage.h:189
static PARSED_COMMAND * ParseRem(void)
Definition: parser.c:576
#define _T(x)
Definition: vfdio.h:22
Definition: cmd.h:304
LIST_ENTRY List
Definition: psmgr.c:57
static TCHAR ParseLine[CMDLINE_LENGTH]
Definition: parser.c:50
static TCHAR * ParsePos
Definition: parser.c:51
#define IFFLAG_NEGATE
Definition: cmd.h:235
TCHAR Filename[]
Definition: cmd.h:364
DWORD error
Definition: parser.c:105
static const TCHAR *const IfOperatorString[]
Definition: parser.c:13
#define STRING(String)
static TCHAR ParseChar(void)
Definition: parser.c:58
BOOL ReadLine(TCHAR *commandline, BOOL bMore)
Definition: cmd.c:1454
Definition: ncftp.h:79
#define FOR_RECURSIVE
Definition: cmd.h:212
static const TCHAR RedirString[][3]
Definition: parser.c:11
Definition: sacdrv.h:277
#define memcpy(s1, s2, n)
Definition: mkisofs.h:878
static PARSED_COMMAND * ParseCommandOp(int OpType)
Definition: parser.c:720
uint32_t DWORD_PTR
Definition: typedefs.h:64
TCHAR * Unparse(PARSED_COMMAND *Cmd, TCHAR *Out, TCHAR *OutEnd)
Definition: parser.c:906
_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
Definition: cmd.h:304
BOOL SubstituteForVars(TCHAR *Src, TCHAR *Dest)
Definition: cmd.c:1391
#define STANDARD_SEPS
Definition: parser.c:31
VOID error_syntax(LPTSTR)
Definition: error.c:154
#define cmd_alloc(size)
Definition: cmddbg.h:29
static TCHAR CurChar
Definition: parser.c:52
#define cmd_free(ptr)
Definition: cmddbg.h:31
void(* Func)(int)
static void ParseError(void)
Definition: parser.c:96
#define cmd_dup(str)
Definition: cmddbg.h:32
#define FIELD_OFFSET(t, f)
Definition: typedefs.h:254
static int InsideBlock
Definition: parser.c:56
static const TCHAR OpString[][3]
Definition: parser.c:9
VOID FreeRedirection(REDIRECTION *)
Definition: redir.c:153
struct _REDIRECTION * Next
Definition: cmd.h:360
Definition: cmd.h:304
#define _istspace
Definition: tchar.h:1504
#define IF_MAX_COMPARISON
#define C_OP_HIGHEST
Definition: parser.c:8
#define memset(x, y, z)
Definition: compat.h:39
#define CHAR(Char)
#define DECLSPEC_NOINLINE
Definition: ntbasedef.h:231
VOID EchoCommand(PARSED_COMMAND *Cmd)
Definition: parser.c:812
static PARSED_COMMAND * ParsePrimary(void)
Definition: parser.c:657