Index: base/shell/cmd/cmd.c =================================================================== --- base/shell/cmd/cmd.c (révision 67026) +++ base/shell/cmd/cmd.c (copie de travail) @@ -538,7 +538,7 @@ param = &com[cl]; /* Skip over whitespace to rest of line, exclude 'echo' command */ - if (_tcsicmp(cmdptr->name, _T("echo")) != 0) + if (_tcsicmp(cmdptr->name, _T("ECHO")) != 0) while (_istspace(*param)) param++; ret = cmdptr->func(param); @@ -751,6 +751,9 @@ case C_FOR: Ret = ExecuteFor(Cmd); break; + case C_REM: + Ret = 1; // or 0?? + break; } UndoRedirection(Cmd->Redirections, NULL); @@ -786,24 +789,24 @@ /* env var doesn't exist, look for a "special" one */ /* %CD% */ - if (_tcsicmp(varName,_T("cd")) ==0) + if (_tcsicmp(varName,_T("CD")) ==0) { GetCurrentDirectory(MAX_PATH, ret); return ret; } /* %TIME% */ - else if (_tcsicmp(varName,_T("time")) ==0) + else if (_tcsicmp(varName,_T("TIME")) ==0) { return GetTimeString(); } /* %DATE% */ - else if (_tcsicmp(varName,_T("date")) ==0) + else if (_tcsicmp(varName,_T("DATE")) ==0) { return GetDateString(); } /* %RANDOM% */ - else if (_tcsicmp(varName,_T("random")) ==0) + else if (_tcsicmp(varName,_T("RANDOM")) ==0) { /* Get random number */ _itot(rand(),ret,10); @@ -811,13 +814,13 @@ } /* %CMDCMDLINE% */ - else if (_tcsicmp(varName,_T("cmdcmdline")) ==0) + else if (_tcsicmp(varName,_T("CMDCMDLINE")) ==0) { return GetCommandLine(); } /* %CMDEXTVERSION% */ - else if (_tcsicmp(varName,_T("cmdextversion")) ==0) + else if (_tcsicmp(varName,_T("CMDEXTVERSION")) ==0) { /* Set version number to 2 */ _itot(2,ret,10); @@ -825,7 +828,7 @@ } /* %ERRORLEVEL% */ - else if (_tcsicmp(varName,_T("errorlevel")) ==0) + else if (_tcsicmp(varName,_T("ERRORLEVEL")) ==0) { _itot(nErrorLevel,ret,10); return ret; Index: base/shell/cmd/cmd.h =================================================================== --- base/shell/cmd/cmd.h (révision 67026) +++ base/shell/cmd/cmd.h (copie de travail) @@ -41,8 +41,8 @@ /* define some error messages */ -#define D_ON _T("on") -#define D_OFF _T("off") +#define D_ON _T("ON") +#define D_OFF _T("OFF") /* command line buffer length */ @@ -360,13 +360,45 @@ /* Prototypes from PARSER.C */ -enum { C_COMMAND, C_QUIET, C_BLOCK, C_MULTI, C_IFFAILURE, C_IFSUCCESS, C_PIPE, C_IF, C_FOR }; +enum { + C_COMMAND, + C_QUIET, // '@' + C_BLOCK, // '(...)' + + C_MULTI, // '&' + C_IFFAILURE, // '||' + C_IFSUCCESS, // '&&' + C_PIPE, // '|' + + C_IF, + C_FOR, + + C_REM +}; typedef struct _PARSED_COMMAND { + /* + * - For C_QUIET, C_BLOCK, C_MULTI, C_IFFAILURE, C_IFSUCCESS and C_PIPE, + * this is the list of commands to be run. + * - For C_IF, Subcommands is the TRUE block and Subcommands->Next is the FALSE block. + * - For C_FOR, this is the command to be repeated. + * - When this PARSED_COMMAND is a command leaf (C_COMMAND), this is NULL. + */ struct _PARSED_COMMAND *Subcommands; + + /* + * For C_QUIET, C_BLOCK, C_MULTI, C_IFFAILURE, C_IFSUCCESS and C_PIPE, + * the linked list of commands is Subcommands and is chained via + * Subcommands->Next + */ struct _PARSED_COMMAND *Next; + + /* List of redirections */ struct _REDIRECTION *Redirections; + + /* Type of parsed command */ BYTE Type; + union { struct @@ -373,19 +405,19 @@ { TCHAR *Rest; TCHAR First[]; - } Command; + } Command; // Used for C_COMMAND and also for C_REM struct { BYTE Flags; BYTE Operator; - TCHAR *LeftArg; - TCHAR *RightArg; + TCHAR *LeftArg; // IF condition - left + TCHAR *RightArg; // IF condition - right. The IF-true block is in Subcommands, the IF-else block is in Next. } If; struct { - BYTE Switches; + BYTE Switches; // Some special "/things" TCHAR Variable; - LPTSTR Params; + LPTSTR Params; // Other "/whatever" things LPTSTR List; struct tagFORCONTEXT *Context; } For; Index: base/shell/cmd/parser.c =================================================================== --- base/shell/cmd/parser.c (révision 67026) +++ base/shell/cmd/parser.c (copie de travail) @@ -12,18 +12,18 @@ static const TCHAR *const IfOperatorString[] = { - _T("cmdextversion"), - _T("defined"), - _T("errorlevel"), - _T("exist"), + _T("CMDEXTVERSION"), + _T("DEFINED"), + _T("ERRORLEVEL"), + _T("EXIST"), #define IF_MAX_UNARY IF_EXIST _T("=="), - _T("equ"), - _T("gtr"), - _T("geq"), - _T("lss"), - _T("leq"), - _T("neq"), + _T("EQU"), + _T("GTR"), + _T("GEQ"), + _T("LSS"), + _T("LEQ"), + _T("NEQ"), #define IF_MAX_COMPARISON IF_NEQ }; @@ -69,7 +69,9 @@ * even separate tokens. */ do + { Char = *ParsePos++; + } while (Char == _T('\r')); if (!Char) @@ -348,7 +350,7 @@ Cmd->If.Flags |= IFFLAG_IGNORECASE; Type = ParseToken(0, STANDARD_SEPS); } - if (_tcsicmp(CurrentToken, _T("not")) == 0) + if (_tcsicmp(CurrentToken, _T("NOT")) == 0) { Cmd->If.Flags |= IFFLAG_NEGATE; Type = ParseToken(0, STANDARD_SEPS); @@ -404,6 +406,7 @@ return NULL; condition_done: + // Cmd->Subcommands == TRUE block Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST); if (Cmd->Subcommands == NULL) { @@ -410,7 +413,8 @@ FreeCommand(Cmd); return NULL; } - if (_tcsicmp(CurrentToken, _T("else")) == 0) + // Cmd->Subcommands->Next == ELSE block + if (_tcsicmp(CurrentToken, _T("ELSE")) == 0) { Cmd->Subcommands->Next = ParseCommandOp(C_OP_LOWEST); if (Cmd->Subcommands->Next == NULL) @@ -484,7 +488,7 @@ Cmd->For.Variable = CurrentToken[1]; ParseToken(0, STANDARD_SEPS); - if (_tcsicmp(CurrentToken, _T("in")) != 0) + if (_tcsicmp(CurrentToken, _T("IN")) != 0) goto error; if (ParseToken(_T('('), STANDARD_SEPS) != TOK_BEGIN_BLOCK) @@ -516,7 +520,7 @@ Cmd->For.List = cmd_dup(List); ParseToken(0, STANDARD_SEPS); - if (_tcsicmp(CurrentToken, _T("do")) != 0) + if (_tcsicmp(CurrentToken, _T("DO")) != 0) goto error; Cmd->Subcommands = ParseCommandOp(C_OP_LOWEST); @@ -537,13 +541,37 @@ /* Parse a REM command */ static PARSED_COMMAND *ParseRem(void) { + TCHAR ParsedLine[CMDLINE_LENGTH]; + PARSED_COMMAND *Cmd; + + // TCHAR *Pos = _stpcpy(ParsedLine, CurrentToken) + 1; + TCHAR *Pos = ParsedLine + ARRAYSIZE(_T("REM")) + 1; + DWORD_PTR TailOffset = Pos - ParsedLine; + + _tcscpy(ParsedLine, _T("REM")); // HACKFIX!! + *Pos++ = _T(' '); + Pos = _stpcpy(Pos, CurrentToken); + *Pos = _T(' '); + /* Just ignore the rest of the line */ while (CurChar && CurChar != _T('\n')) - ParseChar(); - return NULL; + *++Pos = ParseChar(); + *Pos++ = _T('\0'); + CurrentTokenType = TOK_END; + + /* Taken from ParseCommandPart */ + Cmd = cmd_alloc(FIELD_OFFSET(PARSED_COMMAND, Command.First[Pos - ParsedLine])); + Cmd->Type = C_REM; // Special type of C_COMMAND + Cmd->Next = NULL; + Cmd->Subcommands = NULL; + Cmd->Redirections = NULL; + memcpy(Cmd->Command.First, ParsedLine, (Pos - ParsedLine) * sizeof(TCHAR)); + Cmd->Command.Rest = Cmd->Command.First + TailOffset; + + return Cmd; } -static DECLSPEC_NOINLINE PARSED_COMMAND *ParseCommandPart(REDIRECTION *RedirList) +static PARSED_COMMAND *ParseCommandPart(REDIRECTION *RedirList) { TCHAR ParsedLine[CMDLINE_LENGTH]; PARSED_COMMAND *Cmd; @@ -553,9 +581,9 @@ DWORD_PTR TailOffset = Pos - ParsedLine; /* Check for special forms */ - if ((Func = ParseFor, _tcsicmp(ParsedLine, _T("for")) == 0) || - (Func = ParseIf, _tcsicmp(ParsedLine, _T("if")) == 0) || - (Func = ParseRem, _tcsicmp(ParsedLine, _T("rem")) == 0)) + if ((Func = ParseFor, _tcsicmp(ParsedLine, _T("FOR")) == 0) || + (Func = ParseIf, _tcsicmp(ParsedLine, _T("IF")) == 0) || + (Func = ParseRem, _tcsicmp(ParsedLine, _T("REM")) == 0)) { ParseToken(0, STANDARD_SEPS); /* Do special parsing only if it's not followed by /? */ @@ -572,6 +600,8 @@ Pos = _stpcpy(Pos, _T(" /?")); } + /* Normal command */ + /* Now get the tail */ while (1) { @@ -605,6 +635,7 @@ Cmd->Redirections = RedirList; memcpy(Cmd->Command.First, ParsedLine, (Pos - ParsedLine) * sizeof(TCHAR)); Cmd->Command.Rest = Cmd->Command.First + TailOffset; + return Cmd; } @@ -747,6 +778,14 @@ /* * Reconstruct a parse tree into text form; used for echoing * batch file commands and FOR instances. + * + * FIXME!! The problem of this command is this reconstruction feature. + * Indeed on Windows, when a command is echoed, it is the original command + * line that is echoed, so all the case-sensitivity & al. is conserved + * (but it however performs any substitutions before!) + * + * Here on the contrary we loose all this information because we forgot + * about the original command line, we just reconstruct it out of the tree. */ VOID EchoCommand(PARSED_COMMAND *Cmd) @@ -759,9 +798,23 @@ { case C_COMMAND: if (SubstituteForVars(Cmd->Command.First, Buf)) + // ConOutPrintf(_T("First = '%s' ; "), Buf); ConOutPrintf(_T("%s"), Buf); if (SubstituteForVars(Cmd->Command.Rest, Buf)) + // ConOutPrintf(_T("Rest = '%s'"), Buf); ConOutPrintf(_T("%s"), Buf); + + // Wine "I-don't-know-what-I'm-doing-but-I-copy" dumbness... + // (from programs/cmd/wcmdmain.c!WCMD_ReadAndParseLine line 1861) + + /* I don't know why Windows puts a space here but it does */ + /* Except for lines starting with 'echo.' or 'echo:'. Ask MS why */ + if (_tcsnicmp(Cmd->Command.First, _T("ECHO."), 5) != 0 && + _tcsnicmp(Cmd->Command.First, _T("ECHO:"), 5) != 0) + { + ConOutChar(_T(' ')); + } + break; case C_QUIET: return; @@ -785,6 +838,8 @@ } while (Sub); } ConOutChar(_T(')')); + // /* Windows-compatibility: add a space after the closing parenthesis */ + // ConOutChar(_T(' ')); // In fact it seems this happens for *all* commands break; case C_MULTI: case C_IFFAILURE: @@ -796,11 +851,11 @@ EchoCommand(Sub->Next); break; case C_IF: - ConOutPrintf(_T("if")); + ConOutPrintf(_T("IF")); if (Cmd->If.Flags & IFFLAG_IGNORECASE) ConOutPrintf(_T(" /I")); if (Cmd->If.Flags & IFFLAG_NEGATE) - ConOutPrintf(_T(" not")); + ConOutPrintf(_T(" NOT")); if (Cmd->If.LeftArg && SubstituteForVars(Cmd->If.LeftArg, Buf)) ConOutPrintf(_T(" %s"), Buf); ConOutPrintf(_T(" %s"), IfOperatorString[Cmd->If.Operator]); @@ -810,12 +865,12 @@ EchoCommand(Sub); if (Sub->Next) { - ConOutPrintf(_T(" else ")); + ConOutPrintf(_T(" ELSE ")); EchoCommand(Sub->Next); } break; case C_FOR: - ConOutPrintf(_T("for")); + ConOutPrintf(_T("FOR")); if (Cmd->For.Switches & FOR_DIRS) ConOutPrintf(_T(" /D")); if (Cmd->For.Switches & FOR_F) ConOutPrintf(_T(" /F")); if (Cmd->For.Switches & FOR_LOOP) ConOutPrintf(_T(" /L")); @@ -822,9 +877,18 @@ if (Cmd->For.Switches & FOR_RECURSIVE) ConOutPrintf(_T(" /R")); if (Cmd->For.Params) ConOutPrintf(_T(" %s"), Cmd->For.Params); - ConOutPrintf(_T(" %%%c in (%s) do "), Cmd->For.Variable, Cmd->For.List); + ConOutPrintf(_T(" %%%c IN (%s) DO "), Cmd->For.Variable, Cmd->For.List); EchoCommand(Cmd->Subcommands); break; + case C_REM: + // if (SubstituteForVars(Cmd->Command.First, Buf)) + // ConOutPrintf(_T("First = '%s' ; "), Buf); + // ConOutPrintf(_T("First = '%s' ; "), Cmd->Command.First); + ConOutPrintf(_T("%s"), Cmd->Command.First); + if (SubstituteForVars(Cmd->Command.Rest, Buf)) + // ConOutPrintf(_T("Rest = '%s'"), Buf); + ConOutPrintf(_T("%s"), Buf); + break; } for (Redir = Cmd->Redirections; Redir; Redir = Redir->Next) @@ -901,11 +965,11 @@ RECURSE(Sub->Next) break; case C_IF: - STRING(_T("if")) + STRING(_T("IF")) if (Cmd->If.Flags & IFFLAG_IGNORECASE) STRING(_T(" /I")) if (Cmd->If.Flags & IFFLAG_NEGATE) - STRING(_T(" not")) + STRING(_T(" NOT")) if (Cmd->If.LeftArg && SubstituteForVars(Cmd->If.LeftArg, Buf)) PRINTF(_T(" %s"), Buf) PRINTF(_T(" %s"), IfOperatorString[Cmd->If.Operator]); @@ -915,12 +979,12 @@ RECURSE(Sub) if (Sub->Next) { - STRING(_T(" else ")) + STRING(_T(" ELSE ")) RECURSE(Sub->Next) } break; case C_FOR: - STRING(_T("for")) + STRING(_T("FOR")) if (Cmd->For.Switches & FOR_DIRS) STRING(_T(" /D")) if (Cmd->For.Switches & FOR_F) STRING(_T(" /F")) if (Cmd->For.Switches & FOR_LOOP) STRING(_T(" /L")) @@ -927,7 +991,7 @@ if (Cmd->For.Switches & FOR_RECURSIVE) STRING(_T(" /R")) if (Cmd->For.Params) PRINTF(_T(" %s"), Cmd->For.Params) - PRINTF(_T(" %%%c in (%s) do "), Cmd->For.Variable, Cmd->For.List) + PRINTF(_T(" %%%c IN (%s) DO "), Cmd->For.Variable, Cmd->For.List) RECURSE(Cmd->Subcommands) break; } Index: base/shell/cmd/pause.c =================================================================== --- base/shell/cmd/pause.c (révision 67026) +++ base/shell/cmd/pause.c (copie de travail) @@ -44,12 +44,15 @@ return 0; } + /* Display a custom message if specified, or the default one */ if (*param) - ConOutPrintf (param); + ConOutPrintf(param); else - msg_pause (); + msg_pause(); - cgetchar (); + /* Wait for a keypress, then print a newline */ + cgetchar(); + ConOutChar(_T('\n')); return 0; }