ReactOS Fundraising Campaign 2012
 
€ 4,410 / € 30,000

Information | Donate

Home | Info | Community | Development | myReactOS | Contact Us

  1. Home
  2. Community
  3. Development
  4. myReactOS
  5. Fundraiser 2012

  1. Main Page
  2. Alphabetical List
  3. Data Structures
  4. Directories
  5. File List
  6. Data Fields
  7. Globals
  8. Related Pages

ReactOS Development > Doxygen

parser.c
Go to the documentation of this file.
00001 /*
00002  *  PARSER.C - command parsing.
00003  *
00004  */
00005 
00006 #include <precomp.h>
00007 
00008 #define C_OP_LOWEST C_MULTI
00009 #define C_OP_HIGHEST C_PIPE
00010 static const TCHAR OpString[][3] = { _T("&"), _T("||"), _T("&&"), _T("|") };
00011 
00012 static const TCHAR RedirString[][3] = { _T("<"), _T(">"), _T(">>") };
00013 
00014 static const TCHAR *const IfOperatorString[] = {
00015     _T("cmdextversion"),
00016     _T("defined"),
00017     _T("errorlevel"),
00018     _T("exist"),
00019 #define IF_MAX_UNARY IF_EXIST
00020     _T("=="),
00021     _T("equ"),
00022     _T("gtr"),
00023     _T("geq"),
00024     _T("lss"),
00025     _T("leq"),
00026     _T("neq"),
00027 #define IF_MAX_COMPARISON IF_NEQ
00028 };
00029 
00030 /* These three characters act like spaces to the parser in most contexts */
00031 #define STANDARD_SEPS _T(",;=")
00032 
00033 static BOOL IsSeparator(TCHAR Char)
00034 {
00035     return _istspace(Char) || (Char && _tcschr(STANDARD_SEPS, Char));
00036 }
00037 
00038 enum { TOK_END, TOK_NORMAL, TOK_OPERATOR, TOK_REDIRECTION,
00039        TOK_BEGIN_BLOCK, TOK_END_BLOCK };
00040 
00041 static BOOL bParseError;
00042 static BOOL bLineContinuations;
00043 static TCHAR ParseLine[CMDLINE_LENGTH];
00044 static TCHAR *ParsePos;
00045 static TCHAR CurChar;
00046 
00047 static TCHAR CurrentToken[CMDLINE_LENGTH];
00048 static int CurrentTokenType;
00049 static int InsideBlock;
00050 
00051 static TCHAR ParseChar()
00052 {
00053     TCHAR Char;
00054 
00055     if (bParseError)
00056         return CurChar = 0;
00057 
00058 restart:
00059     /* Although CRs can be injected into a line via an environment
00060      * variable substitution, the parser ignores them - they won't
00061      * even separate tokens. */
00062     do
00063         Char = *ParsePos++;
00064     while (Char == _T('\r'));
00065 
00066     if (!Char)
00067     {
00068         ParsePos--;
00069         if (bLineContinuations)
00070         {
00071             if (!ReadLine(ParseLine, TRUE))
00072             {
00073                 /* ^C pressed, or line was too long */
00074                 bParseError = TRUE;
00075             }
00076             else if (*(ParsePos = ParseLine))
00077             {
00078                 goto restart;
00079             }
00080         }
00081     }
00082     return CurChar = Char;
00083 }
00084 
00085 static void ParseError()
00086 {
00087     error_syntax(CurrentTokenType != TOK_END ? CurrentToken : NULL);
00088     bParseError = TRUE;
00089 }
00090 
00091 /* Yes, cmd has a Lexical Analyzer. Whenever the parser gives an "xxx was
00092  * unexpected at this time." message, it shows what the last token read was */
00093 static int ParseToken(TCHAR ExtraEnd, TCHAR *Separators)
00094 {
00095     TCHAR *Out = CurrentToken;
00096     TCHAR Char;
00097     int Type;
00098     BOOL bInQuote = FALSE;
00099 
00100     for (Char = CurChar; Char && Char != _T('\n'); Char = ParseChar())
00101     {
00102         bInQuote ^= (Char == _T('"'));
00103         if (!bInQuote)
00104         {
00105             if (Separators != NULL)
00106             {
00107                 if (_istspace(Char) || _tcschr(Separators, Char))
00108                 {
00109                     /* Skip leading separators */
00110                     if (Out == CurrentToken)
00111                         continue;
00112                     break;
00113                 }
00114             }
00115         
00116             /* Check for numbered redirection */
00117             if ((Char >= _T('0') && Char <= _T('9') &&
00118                    (ParsePos == &ParseLine[1] || IsSeparator(ParsePos[-2]))
00119                    && (*ParsePos == _T('<') || *ParsePos == _T('>'))))
00120             {
00121                 break;
00122             }
00123 
00124             if (Char == ExtraEnd)
00125                 break;
00126             if (InsideBlock && Char == _T(')'))
00127                 break;
00128             if (_tcschr(_T("&|<>"), Char))
00129                 break;
00130 
00131             if (Char == _T('^'))
00132             {
00133                 Char = ParseChar();
00134                 /* Eat up a \n, allowing line continuation */
00135                 if (Char == _T('\n'))
00136                     Char = ParseChar();
00137                 /* Next character is a forced literal */
00138             }
00139         }
00140         if (Out == &CurrentToken[CMDLINE_LENGTH - 1])
00141             break;
00142         *Out++ = Char;
00143     }
00144 
00145     /* Check if we got at least one character before reaching a special one.
00146      * If so, return them and leave the special for the next call. */
00147     if (Out != CurrentToken)
00148     {
00149         Type = TOK_NORMAL;
00150     }
00151     else if (Char == _T('('))
00152     {
00153         Type = TOK_BEGIN_BLOCK;
00154         *Out++ = Char;
00155         ParseChar();
00156     }
00157     else if (Char == _T(')'))
00158     {
00159         Type = TOK_END_BLOCK;
00160         *Out++ = Char;
00161         ParseChar();
00162     }
00163     else if (Char == _T('&') || Char == _T('|'))
00164     {
00165         Type = TOK_OPERATOR;
00166         *Out++ = Char;
00167         Char = ParseChar();
00168         /* check for && or || */
00169         if (Char == Out[-1])
00170         {
00171             *Out++ = Char;
00172             ParseChar();
00173         }
00174     }
00175     else if ((Char >= _T('0') && Char <= _T('9'))
00176              || (Char == _T('<') || Char == _T('>')))
00177     {
00178         Type = TOK_REDIRECTION;
00179         if (Char >= _T('0') && Char <= _T('9'))
00180         {
00181             *Out++ = Char;
00182             Char = ParseChar();
00183         }
00184         *Out++ = Char;
00185         Char = ParseChar();
00186         if (Char == Out[-1])
00187         {
00188             /* Strangely, the tokenizer allows << as well as >>... (it
00189              * will cause an error when trying to parse it though) */
00190             *Out++ = Char;
00191             Char = ParseChar();
00192         }
00193         if (Char == _T('&'))
00194         {
00195             *Out++ = Char;
00196             while (IsSeparator(Char = ParseChar()))
00197                 ;
00198             if (Char >= _T('0') && Char <= _T('9'))
00199             {
00200                 *Out++ = Char;
00201                 ParseChar();
00202             }
00203         }
00204     }
00205     else
00206     {
00207         Type = TOK_END;
00208     }
00209     *Out = _T('\0');
00210     return CurrentTokenType = Type;
00211 }
00212 
00213 static BOOL ParseRedirection(REDIRECTION **List)
00214 {
00215     TCHAR *Tok = CurrentToken;
00216     BYTE Number;
00217     BYTE RedirType;
00218     REDIRECTION *Redir;
00219 
00220     if (*Tok >= _T('0') && *Tok <= _T('9'))
00221         Number = *Tok++ - _T('0');
00222     else
00223         Number = *Tok == _T('<') ? 0 : 1;
00224 
00225     if (*Tok++ == _T('<'))
00226     {
00227         RedirType = REDIR_READ;
00228         if (*Tok == _T('<'))
00229             goto fail;
00230     }
00231     else
00232     {
00233         RedirType = REDIR_WRITE;
00234         if (*Tok == _T('>'))
00235         {
00236             RedirType = REDIR_APPEND;
00237             Tok++;
00238         }
00239     }
00240 
00241     if (!*Tok)
00242     {
00243         /* The file name was not part of this token, so it'll be the next one */
00244         if (ParseToken(0, STANDARD_SEPS) != TOK_NORMAL)
00245             goto fail;
00246         Tok = CurrentToken;
00247     }
00248 
00249     /* If a redirection for this handle number already exists, delete it */
00250     while ((Redir = *List))
00251     {
00252         if (Redir->Number == Number)
00253         {
00254             *List = Redir->Next;
00255             cmd_free(Redir);
00256             continue;
00257         }
00258         List = &Redir->Next;
00259     }
00260 
00261     Redir = cmd_alloc(FIELD_OFFSET(REDIRECTION, Filename[_tcslen(Tok) + 1]));
00262     Redir->Next = NULL;
00263     Redir->OldHandle = INVALID_HANDLE_VALUE;
00264     Redir->Number = Number;
00265     Redir->Type = RedirType;
00266     _tcscpy(Redir->Filename, Tok);
00267     *List = Redir;
00268     return TRUE;
00269 fail:
00270     ParseError();
00271     FreeRedirection(*List);
00272     *List = NULL;
00273     return FALSE;
00274 }
00275 
00276 static PARSED_COMMAND *ParseCommandOp(int OpType);
00277 
00278 /* Parse a parenthesized block */
00279 static PARSED_COMMAND *ParseBlock(REDIRECTION *RedirList)
00280 {
00281     PARSED_COMMAND *Cmd, *Sub, **NextPtr;
00282     Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
00283     Cmd->Type = C_BLOCK;
00284     Cmd->Next = NULL;
00285     Cmd->Subcommands = NULL;
00286     Cmd->Redirections = RedirList;
00287 
00288     /* Read the block contents */
00289     NextPtr = &Cmd->Subcommands;
00290     InsideBlock++;
00291     while (1)
00292     {
00293         Sub = ParseCommandOp(C_OP_LOWEST);
00294         if (Sub)
00295         {
00296             *NextPtr = Sub;
00297             NextPtr = &Sub->Next;
00298         }
00299         else if (bParseError)
00300         {
00301             InsideBlock--;
00302             FreeCommand(Cmd);
00303             return NULL;
00304         }
00305 
00306         if (CurrentTokenType == TOK_END_BLOCK)
00307             break;
00308         /* Skip past the \n */
00309         ParseChar();
00310     }
00311     InsideBlock--;
00312 
00313     /* Process any trailing redirections */
00314     while (ParseToken(0, STANDARD_SEPS) == TOK_REDIRECTION)
00315     {
00316         if (!ParseRedirection(&Cmd->Redirections))
00317         {
00318             FreeCommand(Cmd);
00319             return NULL;
00320         }
00321     }
00322     return Cmd;
00323 }
00324 
00325 /* Parse an IF statement */
00326 static PARSED_COMMAND *ParseIf(void)
00327 {
00328     PARSED_COMMAND *Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
00329     int Type;
00330     memset(Cmd, 0, sizeof(PARSED_COMMAND));
00331     Cmd->Type = C_IF;
00332 
00333     Type = CurrentTokenType;
00334     if (_tcsicmp(CurrentToken, _T("/I")) == 0)
00335     {
00336         Cmd->If.Flags |= IFFLAG_IGNORECASE;
00337         Type = ParseToken(0, STANDARD_SEPS);
00338     }
00339     if (_tcsicmp(CurrentToken, _T("not")) == 0)
00340     {
00341         Cmd->If.Flags |= IFFLAG_NEGATE;
00342         Type = ParseToken(0, STANDARD_SEPS);
00343     }
00344 
00345     if (Type != TOK_NORMAL)
00346     {
00347         FreeCommand(Cmd);
00348         ParseError();
00349         return NULL;
00350     }
00351 
00352     /* Check for unary operators */
00353     for (; Cmd->If.Operator <= IF_MAX_UNARY; Cmd->If.Operator++)
00354     {
00355         if (_tcsicmp(CurrentToken, IfOperatorString[Cmd->If.Operator]) == 0)
00356         {
00357             if (ParseToken(0, STANDARD_SEPS) != TOK_NORMAL)
00358             {
00359                 FreeCommand(Cmd);
00360                 ParseError();
00361                 return NULL;
00362             }
00363             Cmd->If.RightArg = cmd_dup(CurrentToken);
00364             goto condition_done;
00365         }
00366     }
00367 
00368     /* It must be a two-argument (comparison) operator. It could be ==, so
00369      * the equals sign can't be treated as whitespace here. */
00370     Cmd->If.LeftArg = cmd_dup(CurrentToken);
00371     ParseToken(0, _T(",;"));
00372 
00373     /* The right argument can come immediately after == */
00374     if (_tcsnicmp(CurrentToken, _T("=="), 2) == 0 && CurrentToken[2])
00375     {
00376         Cmd->If.RightArg = cmd_dup(&CurrentToken[2]);
00377         goto condition_done;
00378     }
00379 
00380     for (; Cmd->If.Operator <= IF_MAX_COMPARISON; Cmd->If.Operator++)
00381     {
00382         if (_tcsicmp(CurrentToken, IfOperatorString[Cmd->If.Operator]) == 0)
00383         {
00384             if (ParseToken(0, STANDARD_SEPS) != TOK_NORMAL)
00385                 break;
00386             Cmd->If.RightArg = cmd_dup(CurrentToken);
00387             goto condition_done;
00388         }
00389     }
00390     FreeCommand(Cmd);
00391     ParseError();
00392     return NULL;
00393 
00394 condition_done:
00395     Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST);
00396     if (Cmd->Subcommands == NULL)
00397     {
00398         FreeCommand(Cmd);
00399         return NULL;
00400     }
00401     if (_tcsicmp(CurrentToken, _T("else")) == 0)
00402     {
00403         Cmd->Subcommands->Next = ParseCommandOp(C_OP_LOWEST);
00404         if (Cmd->Subcommands->Next == NULL)
00405         {
00406             FreeCommand(Cmd);
00407             return NULL;
00408         }
00409     }
00410 
00411     return Cmd;
00412 }
00413 
00414 /* Parse a FOR command.
00415  * Syntax is: FOR [options] %var IN (list) DO command */
00416 static PARSED_COMMAND *ParseFor(void)
00417 {
00418     PARSED_COMMAND *Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
00419     TCHAR List[CMDLINE_LENGTH];
00420     TCHAR *Pos = List;
00421 
00422     memset(Cmd, 0, sizeof(PARSED_COMMAND));
00423     Cmd->Type = C_FOR;
00424 
00425     while (1)
00426     {
00427         if (_tcsicmp(CurrentToken, _T("/D")) == 0)
00428             Cmd->For.Switches |= FOR_DIRS;
00429         else if (_tcsicmp(CurrentToken, _T("/F")) == 0)
00430         {
00431             Cmd->For.Switches |= FOR_F;
00432             if (!Cmd->For.Params)
00433             {
00434                 ParseToken(0, STANDARD_SEPS);
00435                 if (CurrentToken[0] == _T('/') || CurrentToken[0] == _T('%'))
00436                     break;
00437                 Cmd->For.Params = cmd_dup(CurrentToken);
00438             }
00439         }
00440         else if (_tcsicmp(CurrentToken, _T("/L")) == 0)
00441             Cmd->For.Switches |= FOR_LOOP;
00442         else if (_tcsicmp(CurrentToken, _T("/R")) == 0)
00443         {
00444             Cmd->For.Switches |= FOR_RECURSIVE;
00445             if (!Cmd->For.Params)
00446             {
00447                 ParseToken(0, STANDARD_SEPS);
00448                 if (CurrentToken[0] == _T('/') || CurrentToken[0] == _T('%'))
00449                     break;
00450                 StripQuotes(CurrentToken);
00451                 Cmd->For.Params = cmd_dup(CurrentToken);
00452             }
00453         }
00454         else
00455             break;
00456         ParseToken(0, STANDARD_SEPS);
00457     }
00458 
00459     /* Make sure there aren't two different switches specified
00460      * at the same time, unless they're /D and /R */
00461     if ((Cmd->For.Switches & (Cmd->For.Switches - 1)) != 0
00462         && Cmd->For.Switches != (FOR_DIRS | FOR_RECURSIVE))
00463     {
00464         goto error;
00465     }
00466 
00467     /* Variable name should be % and just one other character */
00468     if (CurrentToken[0] != _T('%') || _tcslen(CurrentToken) != 2)
00469         goto error;
00470     Cmd->For.Variable = CurrentToken[1];
00471 
00472     ParseToken(0, STANDARD_SEPS);
00473     if (_tcsicmp(CurrentToken, _T("in")) != 0)
00474         goto error;
00475 
00476     if (ParseToken(_T('('), STANDARD_SEPS) != TOK_BEGIN_BLOCK)
00477         goto error;
00478 
00479     while (1)
00480     {
00481         int Type;
00482 
00483         /* Pretend we're inside a block so the tokenizer will stop on ')' */
00484         InsideBlock++;
00485         Type = ParseToken(0, STANDARD_SEPS);
00486         InsideBlock--;
00487 
00488         if (Type == TOK_END_BLOCK)
00489             break;
00490 
00491         if (Type != TOK_NORMAL)
00492             goto error;
00493 
00494         if (Pos != List)
00495             *Pos++ = _T(' ');
00496 
00497         if (Pos + _tcslen(CurrentToken) >= &List[CMDLINE_LENGTH])
00498             goto error;
00499         Pos = _stpcpy(Pos, CurrentToken);
00500     }
00501     *Pos = _T('\0');
00502     Cmd->For.List = cmd_dup(List);
00503 
00504     ParseToken(0, STANDARD_SEPS);
00505     if (_tcsicmp(CurrentToken, _T("do")) != 0)
00506         goto error;
00507 
00508     Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST);
00509     if (Cmd->Subcommands == NULL)
00510     {
00511         FreeCommand(Cmd);
00512         return NULL;
00513     }
00514 
00515     return Cmd;
00516 
00517 error:
00518     FreeCommand(Cmd);
00519     ParseError();
00520     return NULL;
00521 }
00522 
00523 /* Parse a REM command */
00524 static PARSED_COMMAND *ParseRem(void)
00525 {
00526     /* Just ignore the rest of the line */
00527     while (CurChar && CurChar != _T('\n'))
00528         ParseChar();
00529     return NULL;
00530 }
00531 
00532 static DECLSPEC_NOINLINE PARSED_COMMAND *ParseCommandPart(REDIRECTION *RedirList)
00533 {
00534     TCHAR ParsedLine[CMDLINE_LENGTH];
00535     PARSED_COMMAND *Cmd;
00536     PARSED_COMMAND *(*Func)(void);
00537 
00538     TCHAR *Pos = _stpcpy(ParsedLine, CurrentToken) + 1;
00539     DWORD_PTR TailOffset = Pos - ParsedLine;
00540 
00541     /* Check for special forms */
00542     if ((Func = ParseFor, _tcsicmp(ParsedLine, _T("for")) == 0) ||
00543         (Func = ParseIf,  _tcsicmp(ParsedLine, _T("if")) == 0)  ||
00544         (Func = ParseRem, _tcsicmp(ParsedLine, _T("rem")) == 0))
00545     {
00546         ParseToken(0, STANDARD_SEPS);
00547         /* Do special parsing only if it's not followed by /? */
00548         if (_tcscmp(CurrentToken, _T("/?")) != 0)
00549         {
00550             if (RedirList)
00551             {
00552                 ParseError();
00553                 FreeRedirection(RedirList);
00554                 return NULL;
00555             }
00556             return Func();
00557         }
00558         Pos = _stpcpy(Pos, _T(" /?"));
00559     }
00560 
00561     /* Now get the tail */
00562     while (1)
00563     {
00564         int Type = ParseToken(0, NULL);
00565         if (Type == TOK_NORMAL)
00566         {
00567             if (Pos + _tcslen(CurrentToken) >= &ParsedLine[CMDLINE_LENGTH])
00568             {
00569                 ParseError();
00570                 FreeRedirection(RedirList);
00571                 return NULL;
00572             }
00573             Pos = _stpcpy(Pos, CurrentToken);
00574         }
00575         else if (Type == TOK_REDIRECTION)
00576         {
00577             if (!ParseRedirection(&RedirList))
00578                 return NULL;
00579         }
00580         else
00581         {
00582             break;
00583         }
00584     }
00585     *Pos++ = _T('\0');
00586 
00587     Cmd = cmd_alloc(FIELD_OFFSET(PARSED_COMMAND, Command.First[Pos - ParsedLine]));
00588     Cmd->Type = C_COMMAND;
00589     Cmd->Next = NULL;
00590     Cmd->Subcommands = NULL;
00591     Cmd->Redirections = RedirList;
00592     memcpy(Cmd->Command.First, ParsedLine, (Pos - ParsedLine) * sizeof(TCHAR));
00593     Cmd->Command.Rest = Cmd->Command.First + TailOffset;
00594     return Cmd;
00595 }
00596 
00597 static PARSED_COMMAND *ParsePrimary(void)
00598 {
00599     REDIRECTION *RedirList = NULL;
00600     int Type;
00601 
00602     while (IsSeparator(CurChar))
00603     {
00604         if (CurChar == _T('\n'))
00605             return NULL;
00606         ParseChar();
00607     }
00608 
00609     if (!CurChar)
00610         return NULL;
00611 
00612     if (CurChar == _T(':'))
00613     {
00614         /* "Ignore" the rest of the line.
00615          * (Line continuations will still be parsed, though.) */
00616         while (ParseToken(0, NULL) != TOK_END)
00617             ;
00618         return NULL;
00619     }
00620 
00621     if (CurChar == _T('@'))
00622     {
00623         PARSED_COMMAND *Cmd;
00624         ParseChar();
00625         Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
00626         Cmd->Type = C_QUIET;
00627         Cmd->Next = NULL;
00628         /* @ acts like a unary operator with low precedence,
00629          * so call the top-level parser */
00630         Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST);
00631         Cmd->Redirections = NULL;
00632         return Cmd;
00633     }
00634 
00635     /* Process leading redirections and get the head of the command */
00636     while ((Type = ParseToken(_T('('), STANDARD_SEPS)) == TOK_REDIRECTION)
00637     {
00638         if (!ParseRedirection(&RedirList))
00639             return NULL;
00640     }
00641 
00642     if (Type == TOK_NORMAL)
00643         return ParseCommandPart(RedirList);
00644     else if (Type == TOK_BEGIN_BLOCK)
00645         return ParseBlock(RedirList);
00646     else if (Type == TOK_END_BLOCK && !RedirList)
00647         return NULL;
00648 
00649     ParseError();
00650     FreeRedirection(RedirList);
00651     return NULL;
00652 }
00653 
00654 static PARSED_COMMAND *ParseCommandOp(int OpType)
00655 {
00656     PARSED_COMMAND *Cmd, *Left, *Right;
00657 
00658     if (OpType == C_OP_HIGHEST)
00659         Cmd = ParsePrimary();
00660     else
00661         Cmd = ParseCommandOp(OpType + 1);
00662 
00663     if (Cmd && !_tcscmp(CurrentToken, OpString[OpType - C_OP_LOWEST]))
00664     {
00665         Left = Cmd;
00666         Right = ParseCommandOp(OpType);
00667         if (!Right)
00668         {
00669             if (!bParseError)
00670             {
00671                 /* & is allowed to have an empty RHS */
00672                 if (OpType == C_MULTI)
00673                     return Left;
00674                 ParseError();
00675             }
00676             FreeCommand(Left);
00677             return NULL;
00678         }
00679 
00680         Cmd = cmd_alloc(sizeof(PARSED_COMMAND));
00681         Cmd->Type = OpType;
00682         Cmd->Next = NULL;
00683         Cmd->Redirections = NULL;
00684         Cmd->Subcommands = Left;
00685         Left->Next = Right;
00686         Right->Next = NULL;
00687     }
00688 
00689     return Cmd;
00690 }
00691 
00692 PARSED_COMMAND *
00693 ParseCommand(LPTSTR Line)
00694 {
00695     PARSED_COMMAND *Cmd;
00696 
00697     if (Line)
00698     {
00699         if (!SubstituteVars(Line, ParseLine, _T('%')))
00700             return NULL;
00701         bLineContinuations = FALSE;
00702     }
00703     else
00704     {
00705         if (!ReadLine(ParseLine, FALSE))
00706             return NULL;
00707         bLineContinuations = TRUE;
00708     }
00709     bParseError = FALSE;
00710     ParsePos = ParseLine;
00711     CurChar = _T(' ');
00712 
00713     Cmd = ParseCommandOp(C_OP_LOWEST);
00714     if (Cmd)
00715     {
00716         if (CurrentTokenType != TOK_END)
00717             ParseError();
00718         if (bParseError)
00719         {
00720             FreeCommand(Cmd);
00721             Cmd = NULL;
00722         }
00723         bIgnoreEcho = FALSE;
00724     }
00725     else
00726     {
00727         bIgnoreEcho = TRUE;
00728     }
00729     return Cmd;
00730 }
00731 
00732 
00733 /* Reconstruct a parse tree into text form; used for echoing
00734  * batch file commands and FOR instances. */
00735 VOID
00736 EchoCommand(PARSED_COMMAND *Cmd)
00737 {
00738     TCHAR Buf[CMDLINE_LENGTH];
00739     PARSED_COMMAND *Sub;
00740     REDIRECTION *Redir;
00741 
00742     switch (Cmd->Type)
00743     {
00744     case C_COMMAND:
00745         if (SubstituteForVars(Cmd->Command.First, Buf))
00746             ConOutPrintf(_T("%s"), Buf);
00747         if (SubstituteForVars(Cmd->Command.Rest, Buf))
00748             ConOutPrintf(_T("%s"), Buf);
00749         break;
00750     case C_QUIET:
00751         return;
00752     case C_BLOCK:
00753         ConOutChar(_T('('));
00754         Sub = Cmd->Subcommands;
00755         if (Sub && !Sub->Next)
00756         {
00757             /* Single-command block: display all on one line */
00758             EchoCommand(Sub);
00759         }
00760         else if (Sub)
00761         {
00762             /* Multi-command block: display parenthesis on separate lines */
00763             ConOutChar(_T('\n'));
00764             do
00765             {
00766                 EchoCommand(Sub);
00767                 ConOutChar(_T('\n'));
00768                 Sub = Sub->Next;
00769             } while (Sub);
00770         }
00771         ConOutChar(_T(')'));
00772         break;
00773     case C_MULTI:
00774     case C_IFFAILURE:
00775     case C_IFSUCCESS:
00776     case C_PIPE:
00777         Sub = Cmd->Subcommands;
00778         EchoCommand(Sub);
00779         ConOutPrintf(_T(" %s "), OpString[Cmd->Type - C_OP_LOWEST]);
00780         EchoCommand(Sub->Next);
00781         break;
00782     case C_IF:
00783         ConOutPrintf(_T("if"));
00784         if (Cmd->If.Flags & IFFLAG_IGNORECASE)
00785             ConOutPrintf(_T(" /I"));
00786         if (Cmd->If.Flags & IFFLAG_NEGATE)
00787             ConOutPrintf(_T(" not"));
00788         if (Cmd->If.LeftArg && SubstituteForVars(Cmd->If.LeftArg, Buf))
00789             ConOutPrintf(_T(" %s"), Buf);
00790         ConOutPrintf(_T(" %s"), IfOperatorString[Cmd->If.Operator]);
00791         if (SubstituteForVars(Cmd->If.RightArg, Buf))
00792             ConOutPrintf(_T(" %s "), Buf);
00793         Sub = Cmd->Subcommands;
00794         EchoCommand(Sub);
00795         if (Sub->Next)
00796         {
00797             ConOutPrintf(_T(" else "));
00798             EchoCommand(Sub->Next);
00799         }
00800         break;
00801     case C_FOR:
00802         ConOutPrintf(_T("for"));
00803         if (Cmd->For.Switches & FOR_DIRS)      ConOutPrintf(_T(" /D"));
00804         if (Cmd->For.Switches & FOR_F)         ConOutPrintf(_T(" /F"));
00805         if (Cmd->For.Switches & FOR_LOOP)      ConOutPrintf(_T(" /L"));
00806         if (Cmd->For.Switches & FOR_RECURSIVE) ConOutPrintf(_T(" /R"));
00807         if (Cmd->For.Params)
00808             ConOutPrintf(_T(" %s"), Cmd->For.Params);
00809         ConOutPrintf(_T(" %%%c in (%s) do "), Cmd->For.Variable, Cmd->For.List);
00810         EchoCommand(Cmd->Subcommands);
00811         break;
00812     }
00813 
00814     for (Redir = Cmd->Redirections; Redir; Redir = Redir->Next)
00815     {
00816         if (SubstituteForVars(Redir->Filename, Buf))
00817             ConOutPrintf(_T(" %c%s%s"), _T('0') + Redir->Number,
00818                 RedirString[Redir->Type], Buf);
00819     }
00820 }
00821 
00822 /* "Unparse" a command into a text form suitable for passing to CMD /C.
00823  * Used for pipes. This is basically the same thing as EchoCommand, but
00824  * writing into a string instead of to standard output. */
00825 TCHAR *
00826 Unparse(PARSED_COMMAND *Cmd, TCHAR *Out, TCHAR *OutEnd)
00827 {
00828     TCHAR Buf[CMDLINE_LENGTH];
00829     PARSED_COMMAND *Sub;
00830     REDIRECTION *Redir;
00831 
00832 /* Since this function has the annoying requirement that it must avoid
00833  * overflowing the supplied buffer, define some helper macros to make
00834  * this less painful */
00835 #define CHAR(Char) { \
00836     if (Out == OutEnd) return NULL; \
00837     *Out++ = Char; }
00838 #define STRING(String) { \
00839     if (Out + _tcslen(String) > OutEnd) return NULL; \
00840     Out = _stpcpy(Out, String); }
00841 #define PRINTF(Format, ...) { \
00842     UINT Len = _sntprintf(Out, OutEnd - Out, Format, __VA_ARGS__); \
00843     if (Len > (UINT)(OutEnd - Out)) return NULL; \
00844     Out += Len; }
00845 #define RECURSE(Subcommand) { \
00846     Out = Unparse(Subcommand, Out, OutEnd); \
00847     if (!Out) return NULL; }
00848 
00849     switch (Cmd->Type)
00850     {
00851     case C_COMMAND:
00852         /* This is fragile since there could be special characters, but
00853          * Windows doesn't bother escaping them, so for compatibility
00854          * we probably shouldn't do it either */
00855         if (!SubstituteForVars(Cmd->Command.First, Buf)) return NULL;
00856         STRING(Buf)
00857         if (!SubstituteForVars(Cmd->Command.Rest, Buf)) return NULL;
00858         STRING(Buf)
00859         break;
00860     case C_QUIET:
00861         CHAR(_T('@'))
00862         RECURSE(Cmd->Subcommands)
00863         break;
00864     case C_BLOCK:
00865         CHAR(_T('('))
00866         for (Sub = Cmd->Subcommands; Sub; Sub = Sub->Next)
00867         {
00868             RECURSE(Sub)
00869             if (Sub->Next)
00870                 CHAR(_T('&'))
00871         }
00872         CHAR(_T(')'))
00873         break;
00874     case C_MULTI:
00875     case C_IFFAILURE:
00876     case C_IFSUCCESS:
00877     case C_PIPE:
00878         Sub = Cmd->Subcommands;
00879         RECURSE(Sub)
00880         PRINTF(_T(" %s "), OpString[Cmd->Type - C_OP_LOWEST])
00881         RECURSE(Sub->Next)
00882         break;
00883     case C_IF:
00884         STRING(_T("if"))
00885         if (Cmd->If.Flags & IFFLAG_IGNORECASE)
00886             STRING(_T(" /I"))
00887         if (Cmd->If.Flags & IFFLAG_NEGATE)
00888             STRING(_T(" not"))
00889         if (Cmd->If.LeftArg && SubstituteForVars(Cmd->If.LeftArg, Buf))
00890             PRINTF(_T(" %s"), Buf)
00891         PRINTF(_T(" %s"), IfOperatorString[Cmd->If.Operator]);
00892         if (!SubstituteForVars(Cmd->If.RightArg, Buf)) return NULL;
00893         PRINTF(_T(" %s "), Buf)
00894         Sub = Cmd->Subcommands;
00895         RECURSE(Sub)
00896         if (Sub->Next)
00897         {
00898             STRING(_T(" else "))
00899             RECURSE(Sub->Next)
00900         }
00901         break;
00902     case C_FOR:
00903         STRING(_T("for"))
00904         if (Cmd->For.Switches & FOR_DIRS)      STRING(_T(" /D"))
00905         if (Cmd->For.Switches & FOR_F)         STRING(_T(" /F"))
00906         if (Cmd->For.Switches & FOR_LOOP)      STRING(_T(" /L"))
00907         if (Cmd->For.Switches & FOR_RECURSIVE) STRING(_T(" /R"))
00908         if (Cmd->For.Params)
00909             PRINTF(_T(" %s"), Cmd->For.Params)
00910         PRINTF(_T(" %%%c in (%s) do "), Cmd->For.Variable, Cmd->For.List)
00911         RECURSE(Cmd->Subcommands)
00912         break;
00913     }
00914 
00915     for (Redir = Cmd->Redirections; Redir; Redir = Redir->Next)
00916     {
00917         if (!SubstituteForVars(Redir->Filename, Buf)) return NULL;
00918         PRINTF(_T(" %c%s%s"), _T('0') + Redir->Number,
00919             RedirString[Redir->Type], Buf)
00920     }
00921     return Out;
00922 }
00923 
00924 VOID
00925 FreeCommand(PARSED_COMMAND *Cmd)
00926 {
00927     if (Cmd->Subcommands)
00928         FreeCommand(Cmd->Subcommands);
00929     if (Cmd->Next)
00930         FreeCommand(Cmd->Next);
00931     FreeRedirection(Cmd->Redirections);
00932     if (Cmd->Type == C_IF)
00933     {
00934         cmd_free(Cmd->If.LeftArg);
00935         cmd_free(Cmd->If.RightArg);
00936     }
00937     else if (Cmd->Type == C_FOR)
00938     {
00939         cmd_free(Cmd->For.Params);
00940         cmd_free(Cmd->For.List);
00941     }
00942     cmd_free(Cmd);
00943 }

Generated on Sun May 27 2012 04:17:19 for ReactOS by doxygen 1.7.6.1

ReactOS is a registered trademark or a trademark of ReactOS Foundation in the United States and other countries.