Home | Info | Community | Development | myReactOS | Contact Us
ReactOS Development > Doxygenparser.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
1.7.6.1
|