This is the third serie related to rewriting cmd's command engine.
Currently, cmd stores block of commands as a linked list of CMD_LIST. Even for nested commands (like 'if' command inside a 'for' command), it's parsed as a single list of commands, using a 'depth' attribute and marker for end of block to describe the structure.
This leads to several issues: - parsing for nested commands is not 100% robust, - chaining of commands (and its precedence) is not handled, - no separation between parsing and execution (esp. for 'IF' and 'FOR' commands, which behave slightly differently than regular commands when dealing with variables with delayed expansion), - data model doesn't 100% fit native behavior (eg redirections can apply to a block of commands, whereas redirections are attached to individual commands) - ...
The final goal is to replace this with: - a tree of commands: + binary nodes for command chaining, + 'IF' and 'FOR' commands being parsed and stored as such + leaf being a command
It will require a couple more series to reach that goal, so this series puts in place the basic building bits: - introducing a new structure CMD_NODE to hold that each node of such a tree, - moving the chaining operator into that CMD_NODE (meaning that we move from a linked list to a binary tree degenerated into a list: left is command, right is next node).
From: Eric Pouech epouech@codeweavers.com
This will allow to separate better the individual command to be executed (CMD_COMMAND) from the chaining and I/O handling of several commands (CMD_NODE).
This also renames CMD_LIST into CMD_COMMAND to fit in above scheme.
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/batch.c | 2 +- programs/cmd/builtins.c | 72 +++++++++--------- programs/cmd/wcmd.h | 30 +++++--- programs/cmd/wcmdmain.c | 163 ++++++++++++++++++++++------------------ 4 files changed, 144 insertions(+), 123 deletions(-)
diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c index 3586cd45731..b68642fc29f 100644 --- a/programs/cmd/batch.c +++ b/programs/cmd/batch.c @@ -86,7 +86,7 @@ void WCMD_batch (WCHAR *file, WCHAR *command, BOOL called, WCHAR *startLabel, HA */
while (context -> skip_rest == FALSE) { - CMD_LIST *toExecute = NULL; /* Commands left to be executed */ + CMD_NODE *toExecute = NULL; /* Commands left to be executed */ if (!WCMD_ReadAndParseLine(NULL, &toExecute, h)) break; /* Note: although this batch program itself may be called, we are not retrying diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index 779df2b65b0..c44dc7ff37a 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -1519,11 +1519,11 @@ void WCMD_echo (const WCHAR *args) * first command to be executed may not be at the front of the * commands->thiscommand string (eg. it may point after a DO or ELSE) */ -static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd, +static void WCMD_part_execute(CMD_NODE **cmdList, const WCHAR *firstcmd, BOOL isIF, BOOL executecmds) { - CMD_LIST *curPosition = *cmdList; - int myDepth = (*cmdList)->bracketDepth; + CMD_NODE *curPosition = *cmdList; + int myDepth = (*cmdList)->single->bracketDepth;
WINE_TRACE("cmdList(%p), firstCmd(%s), doIt(%d), isIF(%d)\n", cmdList, wine_dbgstr_w(firstcmd), executecmds, isIF); @@ -1534,7 +1534,7 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd, /* Process the first command, if there is one */ if (executecmds && firstcmd && *firstcmd) { WCHAR *command = xstrdupW(firstcmd); - WCMD_execute (firstcmd, (*cmdList)->redirects, cmdList, FALSE); + WCMD_execute (firstcmd, (*cmdList)->single->redirects, cmdList, FALSE); free(command); }
@@ -1552,23 +1552,23 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd,
WINE_TRACE("Processing cmdList(%p) - delim(%d) bd(%d / %d) processThese(%d)\n", *cmdList, - (*cmdList)->prevDelim, - (*cmdList)->bracketDepth, + (*cmdList)->single->prevDelim, + (*cmdList)->single->bracketDepth, myDepth, processThese);
/* Execute any statements appended to the line */ /* FIXME: Only if previous call worked for && or failed for || */ - if ((*cmdList)->prevDelim == CMD_ONFAILURE || - (*cmdList)->prevDelim == CMD_ONSUCCESS) { - if (processThese && (*cmdList)->command) { - WCMD_execute ((*cmdList)->command, (*cmdList)->redirects, + if ((*cmdList)->single->prevDelim == CMD_ONFAILURE || + (*cmdList)->single->prevDelim == CMD_ONSUCCESS) { + if (processThese && (*cmdList)->single->command) { + WCMD_execute ((*cmdList)->single->command, (*cmdList)->single->redirects, cmdList, FALSE); } if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand;
/* Execute any appended to the statement with (...) */ - } else if ((*cmdList)->bracketDepth > myDepth) { + } else if ((*cmdList)->single->bracketDepth > myDepth) { if (processThese) { *cmdList = WCMD_process_commands(*cmdList, TRUE, FALSE); } else { @@ -1578,19 +1578,19 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd,
/* End of the command - does 'ELSE ' follow as the next command? */ } else { - if (isIF && WCMD_keyword_ws_found(L"else", (*cmdList)->command)) { + if (isIF && WCMD_keyword_ws_found(L"else", (*cmdList)->single->command)) { /* Swap between if and else processing */ processThese = !executecmds;
/* Process the ELSE part */ if (processThese) { const int keyw_len = lstrlenW(L"else") + 1; - WCHAR *cmd = ((*cmdList)->command) + keyw_len; + WCHAR *cmd = ((*cmdList)->single->command) + keyw_len;
/* Skip leading whitespace between condition and the command */ while (*cmd && (*cmd==' ' || *cmd=='\t')) cmd++; if (*cmd) { - WCMD_execute (cmd, (*cmdList)->redirects, cmdList, FALSE); + WCMD_execute (cmd, (*cmdList)->single->redirects, cmdList, FALSE); } } else { /* Loop skipping all commands until we get back to the current @@ -1599,8 +1599,8 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd, do { *cmdList = (*cmdList)->nextcommand; } while (*cmdList && - ((*cmdList)->bracketDepth > myDepth || - (*cmdList)->prevDelim)); + ((*cmdList)->single->bracketDepth > myDepth || + (*cmdList)->single->prevDelim));
/* After the else is complete, we need to now process subsequent commands */ processThese = TRUE; @@ -1610,8 +1610,8 @@ static void WCMD_part_execute(CMD_LIST **cmdList, const WCHAR *firstcmd, /* If we were in an IF statement and we didn't find an else and yet we get back to the same bracket depth as the IF, then the IF statement is over. This is required to handle nested ifs properly */ - } else if (isIF && (*cmdList)->bracketDepth == myDepth) { - if (WCMD_keyword_ws_found(L"do", (*cmdList)->command)) { + } else if (isIF && (*cmdList)->single->bracketDepth == myDepth) { + if (WCMD_keyword_ws_found(L"do", (*cmdList)->single->command)) { WINE_TRACE("Still inside FOR-loop, not an end of IF statement\n"); *cmdList = (*cmdList)->nextcommand; } else { @@ -1935,9 +1935,9 @@ static int WCMD_for_nexttoken(int lasttoken, WCHAR *tokenstr, * forf_delims [I] - The delimiters to use when breaking the string apart * forf_tokens [I] - The tokens to use when breaking the string apart */ -static void WCMD_parse_line(CMD_LIST *cmdStart, +static void WCMD_parse_line(CMD_NODE *cmdStart, const WCHAR *firstCmd, - CMD_LIST **cmdEnd, + CMD_NODE **cmdEnd, const WCHAR variable, WCHAR *buffer, BOOL *doExecuted, @@ -2024,7 +2024,7 @@ static void WCMD_parse_line(CMD_LIST *cmdStart,
/* Execute the body of the foor loop with these values */ if (varidx >= 0 && forloopcontext.variable[varidx] && forloopcontext.variable[varidx][0] != forf_eol) { - CMD_LIST *thisCmdStart = cmdStart; + CMD_NODE *thisCmdStart = cmdStart; *doExecuted = TRUE; WCMD_part_execute(&thisCmdStart, firstCmd, FALSE, TRUE); *cmdEnd = thisCmdStart; @@ -2108,13 +2108,13 @@ static FILE* WCMD_forf_getinput(BOOL usebackq, WCHAR *itemstr, BOOL iscmd) * */
-void WCMD_for (WCHAR *p, CMD_LIST **cmdList) { +void WCMD_for (WCHAR *p, CMD_NODE **cmdList) {
WIN32_FIND_DATAW fd; HANDLE hff; int i; const int in_len = lstrlenW(L"in"); - CMD_LIST *setStart, *thisSet, *cmdStart, *cmdEnd; + CMD_NODE *setStart, *thisSet, *cmdStart, *cmdEnd; WCHAR variable[4]; int varidx = -1; WCHAR *oldvariablevalue; @@ -2129,7 +2129,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) { BOOL doExecuted = FALSE; /* Has the 'do' part been executed */ LONG numbers[3] = {0,0,0}; /* Defaults to 0 in native */ int itemNum; - CMD_LIST *thisCmdStart; + CMD_NODE *thisCmdStart; int parameterNo = 0; WCHAR forf_eol = 0; int forf_skip = 0; @@ -2219,15 +2219,15 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) { }
/* Save away where the set of data starts and the variable */ - thisDepth = (*cmdList)->bracketDepth; + thisDepth = (*cmdList)->single->bracketDepth; *cmdList = (*cmdList)->nextcommand; setStart = (*cmdList);
/* Skip until the close bracket */ WINE_TRACE("Searching %p as the set\n", *cmdList); while (*cmdList && - (*cmdList)->command != NULL && - (*cmdList)->bracketDepth > thisDepth) { + (*cmdList)->single->command != NULL && + (*cmdList)->single->bracketDepth > thisDepth) { WINE_TRACE("Skipping %p which is part of the set\n", *cmdList); *cmdList = (*cmdList)->nextcommand; } @@ -2238,7 +2238,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) { /* Syntax error if missing close bracket, or nothing following it and once we have the complete set, we expect a DO */ WINE_TRACE("Looking for 'do ' in %p\n", *cmdList); - if ((*cmdList == NULL) || !WCMD_keyword_ws_found(L"do", (*cmdList)->command)) { + if ((*cmdList == NULL) || !WCMD_keyword_ws_found(L"do", (*cmdList)->single->command)) { WCMD_output_stderr (WCMD_LoadMessage(WCMD_SYNTAXERR)); return; } @@ -2252,7 +2252,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) { /* Save away the starting position for the commands (and offset for the first one) */ cmdStart = *cmdList; - firstCmd = (*cmdList)->command + 3; /* Skip 'do ' */ + firstCmd = (*cmdList)->single->command + 3; /* Skip 'do ' */ itemNum = 0;
/* If we are recursing directories (ie /R), add all sub directories now, then @@ -2262,8 +2262,8 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) { thisSet = setStart; /* Loop through all set entries */ while (thisSet && - thisSet->command != NULL && - thisSet->bracketDepth >= thisDepth) { + thisSet->single->command != NULL && + thisSet->single->bracketDepth >= thisDepth) {
/* Loop through all entries on the same line */ WCHAR *staticitem; @@ -2272,7 +2272,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) {
WINE_TRACE("Processing for set %p\n", thisSet); i = 0; - while (*(staticitem = WCMD_parameter (thisSet->command, i, &itemStart, TRUE, FALSE))) { + while (*(staticitem = WCMD_parameter (thisSet->single->command, i, &itemStart, TRUE, FALSE))) {
/* * If the parameter within the set has a wildcard then search for matching files @@ -2516,7 +2516,7 @@ void WCMD_for (WCHAR *p, CMD_LIST **cmdList) { value needs to be the NEXT command to execute, which it either is, or we need to step over the closing bracket */ *cmdList = cmdEnd; - if (cmdEnd && cmdEnd->command == NULL) *cmdList = cmdEnd->nextcommand; + if (cmdEnd && cmdEnd->single->command == NULL) *cmdList = cmdEnd->nextcommand; }
/************************************************************************** @@ -2567,7 +2567,7 @@ void WCMD_give_help (const WCHAR *args) * FIXME: DOS is supposed to allow labels with spaces - we don't. */
-void WCMD_goto (CMD_LIST **cmdList) { +void WCMD_goto (CMD_NODE **cmdList) {
WCHAR string[MAX_PATH]; WCHAR *labelend = NULL; @@ -2903,7 +2903,7 @@ syntax_err: * * FIXME: Much more syntax checking needed! */ -void WCMD_if (WCHAR *p, CMD_LIST **cmdList) +void WCMD_if (WCHAR *p, CMD_NODE **cmdList) { int negate; /* Negate condition */ int test; /* Condition evaluation result */ @@ -4761,7 +4761,7 @@ int WCMD_volume(BOOL set_label, const WCHAR *path) * */
-void WCMD_exit (CMD_LIST **cmdList) { +void WCMD_exit (CMD_NODE **cmdList) { int rc = wcstol(param1, NULL, 10); /* Note: wcstol of empty parameter is 0 */
if (context && lstrcmpiW(quals, L"/B") == 0) { diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index 72e5ef1ad36..7dcea05e557 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -45,14 +45,20 @@ typedef enum _CMDdelimiters {
/* Data structure to hold commands to be processed */
-typedef struct _CMD_LIST { +typedef struct _CMD_COMMAND +{ WCHAR *command; /* Command string to execute */ WCHAR *redirects; /* Redirects in place */ - struct _CMD_LIST *nextcommand; /* Next command string to execute */ CMD_DELIMITERS prevDelim; /* Previous delimiter */ int bracketDepth;/* How deep bracketing have we got to */ WCHAR pipeFile[MAX_PATH]; /* Where to get input from for pipes */ -} CMD_LIST; +} CMD_COMMAND; + +typedef struct _CMD_NODE +{ + CMD_COMMAND *single; + struct _CMD_NODE *nextcommand; /* Next command string to execute */ +} CMD_NODE;
void WCMD_assoc (const WCHAR *, BOOL); void WCMD_batch (WCHAR *, WCHAR *, BOOL, WCHAR *, HANDLE); @@ -68,12 +74,12 @@ void WCMD_directory (WCHAR *); void WCMD_echo (const WCHAR *); void WCMD_endlocal (void); void WCMD_enter_paged_mode(const WCHAR *); -void WCMD_exit (CMD_LIST **cmdList); -void WCMD_for (WCHAR *, CMD_LIST **cmdList); +void WCMD_exit (CMD_NODE **cmdList); +void WCMD_for (WCHAR *, CMD_NODE **cmdList); BOOL WCMD_get_fullpath(const WCHAR *, SIZE_T, WCHAR *, WCHAR **); void WCMD_give_help (const WCHAR *args); -void WCMD_goto (CMD_LIST **cmdList); -void WCMD_if (WCHAR *, CMD_LIST **cmdList); +void WCMD_goto (CMD_NODE **cmdList); +void WCMD_if (WCHAR *, CMD_NODE **cmdList); void WCMD_leave_paged_mode(void); void WCMD_more (WCHAR *); void WCMD_move (void); @@ -118,11 +124,11 @@ WCHAR *WCMD_LoadMessage(UINT id); void WCMD_strsubstW(WCHAR *start, const WCHAR* next, const WCHAR* insert, int len); BOOL WCMD_ReadFile(const HANDLE hIn, WCHAR *intoBuf, const DWORD maxChars, LPDWORD charsRead);
-WCHAR *WCMD_ReadAndParseLine(const WCHAR *initialcmd, CMD_LIST **output, HANDLE readFrom); -CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket, BOOL retrycall); -void WCMD_free_commands(CMD_LIST *cmds); +WCHAR *WCMD_ReadAndParseLine(const WCHAR *initialcmd, CMD_NODE **output, HANDLE readFrom); +CMD_NODE *WCMD_process_commands(CMD_NODE *thisCmd, BOOL oneBracket, BOOL retrycall); +void WCMD_free_commands(CMD_NODE *cmds); void WCMD_execute (const WCHAR *orig_command, const WCHAR *redirects, - CMD_LIST **cmdList, BOOL retrycall); + CMD_NODE **cmdList, BOOL retrycall);
void *xrealloc(void *, size_t) __WINE_ALLOC_SIZE(2) __WINE_DEALLOC(free) __WINE_MALLOC;
@@ -162,7 +168,7 @@ typedef struct _BATCH_CONTEXT { int shift_count[10]; /* Offset in terms of shifts for %0 - %9 */ struct _BATCH_CONTEXT *prev_context; /* Pointer to the previous context block */ BOOL skip_rest; /* Skip the rest of the batch program and exit */ - CMD_LIST *toExecute; /* Commands left to be executed */ + CMD_NODE *toExecute; /* Commands left to be executed */ } BATCH_CONTEXT;
/* Data structure to handle building lists during recursive calls */ diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 128610b0800..abef0f7e5f4 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -1250,7 +1250,7 @@ void WCMD_run_program (WCHAR *command, BOOL called)
/* Not found anywhere - were we called? */ if (called) { - CMD_LIST *toExecute = NULL; /* Commands left to be executed */ + CMD_NODE *toExecute = NULL; /* Commands left to be executed */
/* Parse the command string, without reading any more input */ WCMD_ReadAndParseLine(command, &toExecute, INVALID_HANDLE_VALUE); @@ -1278,7 +1278,7 @@ void WCMD_run_program (WCHAR *command, BOOL called) * we are attempting this retry. */ void WCMD_execute (const WCHAR *command, const WCHAR *redirects, - CMD_LIST **cmdList, BOOL retrycall) + CMD_NODE **cmdList, BOOL retrycall) { WCHAR *cmd, *parms_start, *redir; WCHAR *pos; @@ -1335,7 +1335,7 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects, a change to not wait for the first app to finish but rather the pipe */ if (!(cmd_index == WCMD_FOR || cmd_index == WCMD_IF) && cmdList && (*cmdList)->nextcommand && - (*cmdList)->nextcommand->prevDelim == CMD_PIPE) { + (*cmdList)->nextcommand->single->prevDelim == CMD_PIPE) {
WCHAR temp_path[MAX_PATH];
@@ -1345,14 +1345,14 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
/* Generate a unique temporary filename */ GetTempPathW(ARRAY_SIZE(temp_path), temp_path); - GetTempFileNameW(temp_path, L"CMD", 0, (*cmdList)->nextcommand->pipeFile); + GetTempFileNameW(temp_path, L"CMD", 0, (*cmdList)->nextcommand->single->pipeFile); WINE_TRACE("Using temporary file of %s\n", - wine_dbgstr_w((*cmdList)->nextcommand->pipeFile)); + wine_dbgstr_w((*cmdList)->nextcommand->single->pipeFile)); }
/* If piped output, send stdout to the pipe by appending >filename to redirects */ if (piped) { - wsprintfW (new_redir, L"%s > %s", redirects, (*cmdList)->nextcommand->pipeFile); + wsprintfW (new_redir, L"%s > %s", redirects, (*cmdList)->nextcommand->single->pipeFile); WINE_TRACE("Redirects now %s\n", wine_dbgstr_w(new_redir)); } else { lstrcpyW(new_redir, redirects); @@ -1404,9 +1404,9 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects, */ if (!(cmd_index == WCMD_FOR || cmd_index == WCMD_IF)) { /* STDIN could come from a preceding pipe, so delete on close if it does */ - if (cmdList && (*cmdList)->pipeFile[0] != 0x00) { - WINE_TRACE("Input coming from %s\n", wine_dbgstr_w((*cmdList)->pipeFile)); - h = CreateFileW((*cmdList)->pipeFile, GENERIC_READ, + if (cmdList && (*cmdList)->single->pipeFile[0] != 0x00) { + WINE_TRACE("Input coming from %s\n", wine_dbgstr_w((*cmdList)->single->pipeFile)); + h = CreateFileW((*cmdList)->single->pipeFile, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL); if (h == INVALID_HANDLE_VALUE) { @@ -1418,7 +1418,7 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects, SetStdHandle (STD_INPUT_HANDLE, h);
/* No need to remember the temporary name any longer once opened */ - (*cmdList)->pipeFile[0] = 0x00; + (*cmdList)->single->pipeFile[0] = 0x00;
/* Otherwise STDIN could come from a '<' redirect */ } else if ((pos = wcschr(new_redir,'<')) != NULL) { @@ -1674,18 +1674,18 @@ WCHAR *WCMD_LoadMessage(UINT id) { * * Dumps out the parsed command line to ensure syntax is correct */ -static void WCMD_DumpCommands(CMD_LIST *commands) { - CMD_LIST *thisCmd = commands; +static void WCMD_DumpCommands(CMD_NODE *commands) { + CMD_NODE *thisCmd = commands;
WINE_TRACE("Parsed line:\n"); while (thisCmd != NULL) { WINE_TRACE("%p %d %2.2d %p %s Redir:%s\n", thisCmd, - thisCmd->prevDelim, - thisCmd->bracketDepth, + thisCmd->single->prevDelim, + thisCmd->single->bracketDepth, thisCmd->nextcommand, - wine_dbgstr_w(thisCmd->command), - wine_dbgstr_w(thisCmd->redirects)); + wine_dbgstr_w(thisCmd->single->command), + wine_dbgstr_w(thisCmd->single->redirects)); thisCmd = thisCmd->nextcommand; } } @@ -1695,16 +1695,15 @@ static void WCMD_DumpCommands(CMD_LIST *commands) { * * Adds a command to the current command list */ -static void WCMD_addCommand(WCHAR *command, int *commandLen, - WCHAR *redirs, int *redirLen, - WCHAR **copyTo, int **copyToLen, - CMD_DELIMITERS prevDelim, int curDepth, - CMD_LIST **output) { - - CMD_LIST *thisEntry = NULL; +static CMD_COMMAND *WCMD_createCommand(WCHAR *command, int *commandLen, + WCHAR *redirs, int *redirLen, + WCHAR **copyTo, int **copyToLen, + CMD_DELIMITERS prevDelim, int curDepth) + { + CMD_COMMAND *thisEntry = NULL;
/* Allocate storage for command */ - thisEntry = xalloc(sizeof(CMD_LIST)); + thisEntry = xalloc(sizeof(CMD_COMMAND));
/* Copy in the command */ if (command) { @@ -1731,14 +1730,23 @@ static void WCMD_addCommand(WCHAR *command, int *commandLen, }
/* Fill in other fields */ - thisEntry->nextcommand = NULL; thisEntry->prevDelim = prevDelim; thisEntry->bracketDepth = curDepth; - - for (; *output; output = &((*output)->nextcommand)) {} - *output = thisEntry; + return thisEntry; }
+static void WCMD_appendCommand(CMD_COMMAND *command, CMD_NODE **node) +{ + CMD_NODE *new = xalloc(sizeof(CMD_NODE)); + + if (new) + { + new->single = command; + new->nextcommand = NULL; + for (; *node; node = &((*node)->nextcommand)) {} + *node = new; + } +}
/*************************************************************************** * WCMD_IsEndQuote @@ -1808,7 +1816,7 @@ static BOOL WCMD_IsEndQuote(const WCHAR *quote, int quoteIndex) * - Anything else gets put into the command string (including * redirects) */ -WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE readFrom) +WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE readFrom) { WCHAR *curPos; int inQuotes = 0; @@ -1819,6 +1827,7 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE WCHAR *curCopyTo; int *curLen; int curDepth = 0; + CMD_COMMAND *single_cmd = NULL; CMD_DELIMITERS prevDelim = CMD_NONE; static WCHAR *extraSpace = NULL; /* Deliberately never freed */ BOOL inOneLine = FALSE; @@ -1848,6 +1857,7 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE return NULL; }
+ *output = NULL; /* If initial command read in, use that, otherwise get input from handle */ if (optionalcmd != NULL) { lstrcpyW(extraSpace, optionalcmd); @@ -2086,12 +2096,11 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE if (curStringLen > 0) {
/* Add the current command */ - WCMD_addCommand(curString, &curStringLen, - curRedirs, &curRedirsLen, - &curCopyTo, &curLen, - prevDelim, curDepth, - output); - + single_cmd = WCMD_createCommand(curString, &curStringLen, + curRedirs, &curRedirsLen, + &curCopyTo, &curLen, + prevDelim, curDepth); + WCMD_appendCommand(single_cmd, output); }
if (*(curPos+1) == '|') { @@ -2160,11 +2169,11 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE }
/* Add the current command */ - WCMD_addCommand(curString, &curStringLen, - curRedirs, &curRedirsLen, - &curCopyTo, &curLen, - prevDelim, curDepth, - output); + single_cmd = WCMD_createCommand(curString, &curStringLen, + curRedirs, &curRedirsLen, + &curCopyTo, &curLen, + prevDelim, curDepth); + WCMD_appendCommand(single_cmd, output);
curDepth++; } else { @@ -2191,12 +2200,11 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE if (curStringLen > 0) {
/* Add the current command */ - WCMD_addCommand(curString, &curStringLen, - curRedirs, &curRedirsLen, - &curCopyTo, &curLen, - prevDelim, curDepth, - output); - + single_cmd = WCMD_createCommand(curString, &curStringLen, + curRedirs, &curRedirsLen, + &curCopyTo, &curLen, + prevDelim, curDepth); + WCMD_appendCommand(single_cmd, output); }
if (*(curPos+1) == '&') { @@ -2224,20 +2232,21 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE if (curStringLen) {
/* Add the current command */ - WCMD_addCommand(curString, &curStringLen, - curRedirs, &curRedirsLen, - &curCopyTo, &curLen, - prevDelim, curDepth, - output); + single_cmd = WCMD_createCommand(curString, &curStringLen, + curRedirs, &curRedirsLen, + &curCopyTo, &curLen, + prevDelim, curDepth); + WCMD_appendCommand(single_cmd, output); }
/* Add an empty entry to the command list */ prevDelim = CMD_NONE; - WCMD_addCommand(NULL, &curStringLen, - curRedirs, &curRedirsLen, - &curCopyTo, &curLen, - prevDelim, curDepth, - output); + single_cmd = WCMD_createCommand(NULL, &curStringLen, + curRedirs, &curRedirsLen, + &curCopyTo, &curLen, + prevDelim, curDepth); + WCMD_appendCommand(single_cmd, output); + curDepth--;
/* Leave inIn if necessary */ @@ -2269,11 +2278,11 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE if (*curPos == 0x00 && !lastWasCaret && *curLen > 0) {
/* Add an entry to the command list */ - WCMD_addCommand(curString, &curStringLen, - curRedirs, &curRedirsLen, - &curCopyTo, &curLen, - prevDelim, curDepth, - output); + single_cmd = WCMD_createCommand(curString, &curStringLen, + curRedirs, &curRedirsLen, + &curCopyTo, &curLen, + prevDelim, curDepth); + WCMD_appendCommand(single_cmd, output);
/* If we had a single line if or else, and we pretended to add brackets, end them now */ @@ -2355,22 +2364,22 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_LIST **output, HANDLE * * Process all the commands read in so far */ -CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket, +CMD_NODE *WCMD_process_commands(CMD_NODE *thisCmd, BOOL oneBracket, BOOL retrycall) {
int bdepth = -1;
- if (thisCmd && oneBracket) bdepth = thisCmd->bracketDepth; + if (thisCmd && oneBracket) bdepth = thisCmd->single->bracketDepth;
/* Loop through the commands, processing them one by one */ while (thisCmd) {
- CMD_LIST *origCmd = thisCmd; + CMD_NODE *origCmd = thisCmd;
/* If processing one bracket only, and we find the end bracket entry (or less), return */ - if (oneBracket && !thisCmd->command && - bdepth <= thisCmd->bracketDepth) { + if (oneBracket && !thisCmd->single->command && + bdepth <= thisCmd->single->bracketDepth) { WINE_TRACE("Finished bracket @ %p, next command is %p\n", thisCmd, thisCmd->nextcommand); return thisCmd->nextcommand; @@ -2379,9 +2388,9 @@ CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket, /* Ignore the NULL entries a ')' inserts (Only 'if' cares about them and it will be handled in there) Also, skip over any batch labels (eg. :fred) */ - if (thisCmd->command && thisCmd->command[0] != ':') { - WINE_TRACE("Executing command: '%s'\n", wine_dbgstr_w(thisCmd->command)); - WCMD_execute (thisCmd->command, thisCmd->redirects, &thisCmd, retrycall); + if (thisCmd->single->command && thisCmd->single->command[0] != ':') { + WINE_TRACE("Executing command: '%s'\n", wine_dbgstr_w(thisCmd->single->command)); + WCMD_execute (thisCmd->single->command, thisCmd->single->redirects, &thisCmd, retrycall); }
/* Step on unless the command itself already stepped on */ @@ -2390,6 +2399,13 @@ CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket, return NULL; }
+static void WCMD_free_command(CMD_COMMAND *cmd) +{ + free(cmd->command); + free(cmd->redirects); + free(cmd); +} + /*************************************************************************** * WCMD_free_commands * @@ -2398,14 +2414,13 @@ CMD_LIST *WCMD_process_commands(CMD_LIST *thisCmd, BOOL oneBracket, * pointer will be modified within the commands, and hence a single free * routine is simpler */ -void WCMD_free_commands(CMD_LIST *cmds) { +void WCMD_free_commands(CMD_NODE *cmds) {
/* Loop through the commands, freeing them one by one */ while (cmds) { - CMD_LIST *thisCmd = cmds; + CMD_NODE *thisCmd = cmds; cmds = cmds->nextcommand; - free(thisCmd->command); - free(thisCmd->redirects); + WCMD_free_command(thisCmd->single); free(thisCmd); } } @@ -2432,7 +2447,7 @@ int __cdecl wmain (int argc, WCHAR *argvW[]) BOOL opt_q; int opt_t = 0; WCHAR comspec[MAX_PATH]; - CMD_LIST *toExecute = NULL; /* Commands left to be executed */ + CMD_NODE *toExecute = NULL; /* Commands left to be executed */ RTL_OSVERSIONINFOEXW osv; char osver[50]; STARTUPINFOW startupInfo;
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/builtins.c | 72 ++++++++++++++++++++--------------------- programs/cmd/wcmd.h | 18 ++++++++++- programs/cmd/wcmdmain.c | 52 ++++++++++++++--------------- 3 files changed, 79 insertions(+), 63 deletions(-)
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index c44dc7ff37a..4a012166e25 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -1523,7 +1523,7 @@ static void WCMD_part_execute(CMD_NODE **cmdList, const WCHAR *firstcmd, BOOL isIF, BOOL executecmds) { CMD_NODE *curPosition = *cmdList; - int myDepth = (*cmdList)->single->bracketDepth; + int myDepth = CMD_node_get_depth(*cmdList);
WINE_TRACE("cmdList(%p), firstCmd(%s), doIt(%d), isIF(%d)\n", cmdList, wine_dbgstr_w(firstcmd), executecmds, isIF); @@ -1534,13 +1534,13 @@ static void WCMD_part_execute(CMD_NODE **cmdList, const WCHAR *firstcmd, /* Process the first command, if there is one */ if (executecmds && firstcmd && *firstcmd) { WCHAR *command = xstrdupW(firstcmd); - WCMD_execute (firstcmd, (*cmdList)->single->redirects, cmdList, FALSE); + WCMD_execute (firstcmd, CMD_node_get_command(*cmdList)->redirects, cmdList, FALSE); free(command); }
/* If it didn't move the position, step to next command */ - if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand; + if (curPosition == *cmdList) *cmdList = CMD_node_next(*cmdList);
/* Process any other parts of the command */ if (*cmdList) { @@ -1552,74 +1552,74 @@ static void WCMD_part_execute(CMD_NODE **cmdList, const WCHAR *firstcmd,
WINE_TRACE("Processing cmdList(%p) - delim(%d) bd(%d / %d) processThese(%d)\n", *cmdList, - (*cmdList)->single->prevDelim, - (*cmdList)->single->bracketDepth, + CMD_node_get_command(*cmdList)->prevDelim, + CMD_node_get_depth(*cmdList), myDepth, processThese);
/* Execute any statements appended to the line */ /* FIXME: Only if previous call worked for && or failed for || */ - if ((*cmdList)->single->prevDelim == CMD_ONFAILURE || - (*cmdList)->single->prevDelim == CMD_ONSUCCESS) { - if (processThese && (*cmdList)->single->command) { - WCMD_execute ((*cmdList)->single->command, (*cmdList)->single->redirects, + if (CMD_node_get_command(*cmdList)->prevDelim == CMD_ONFAILURE || + CMD_node_get_command(*cmdList)->prevDelim == CMD_ONSUCCESS) { + if (processThese && CMD_node_get_command(*cmdList)->command) { + WCMD_execute (CMD_node_get_command(*cmdList)->command, CMD_node_get_command(*cmdList)->redirects, cmdList, FALSE); } - if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand; + if (curPosition == *cmdList) *cmdList = CMD_node_next(*cmdList);
/* Execute any appended to the statement with (...) */ - } else if ((*cmdList)->single->bracketDepth > myDepth) { + } else if (CMD_node_get_depth(*cmdList) > myDepth) { if (processThese) { *cmdList = WCMD_process_commands(*cmdList, TRUE, FALSE); } else { WINE_TRACE("Skipping command %p due to stack depth\n", *cmdList); } - if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand; + if (curPosition == *cmdList) *cmdList = CMD_node_next(*cmdList);
/* End of the command - does 'ELSE ' follow as the next command? */ } else { - if (isIF && WCMD_keyword_ws_found(L"else", (*cmdList)->single->command)) { + if (isIF && WCMD_keyword_ws_found(L"else", CMD_node_get_command(*cmdList)->command)) { /* Swap between if and else processing */ processThese = !executecmds;
/* Process the ELSE part */ if (processThese) { const int keyw_len = lstrlenW(L"else") + 1; - WCHAR *cmd = ((*cmdList)->single->command) + keyw_len; + WCHAR *cmd = (CMD_node_get_command(*cmdList)->command) + keyw_len;
/* Skip leading whitespace between condition and the command */ while (*cmd && (*cmd==' ' || *cmd=='\t')) cmd++; if (*cmd) { - WCMD_execute (cmd, (*cmdList)->single->redirects, cmdList, FALSE); + WCMD_execute (cmd, CMD_node_get_command(*cmdList)->redirects, cmdList, FALSE); } } else { /* Loop skipping all commands until we get back to the current depth, including skipping commands and their subsequent pipes (eg cmd | prog) */ do { - *cmdList = (*cmdList)->nextcommand; + *cmdList = CMD_node_next(*cmdList); } while (*cmdList && - ((*cmdList)->single->bracketDepth > myDepth || - (*cmdList)->single->prevDelim)); + (CMD_node_get_depth(*cmdList) > myDepth || + CMD_node_get_command(*cmdList)->prevDelim));
/* After the else is complete, we need to now process subsequent commands */ processThese = TRUE; } - if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand; + if (curPosition == *cmdList) *cmdList = CMD_node_next(*cmdList);
/* If we were in an IF statement and we didn't find an else and yet we get back to the same bracket depth as the IF, then the IF statement is over. This is required to handle nested ifs properly */ - } else if (isIF && (*cmdList)->single->bracketDepth == myDepth) { - if (WCMD_keyword_ws_found(L"do", (*cmdList)->single->command)) { + } else if (isIF && CMD_node_get_depth(*cmdList) == myDepth) { + if (WCMD_keyword_ws_found(L"do", CMD_node_get_command(*cmdList)->command)) { WINE_TRACE("Still inside FOR-loop, not an end of IF statement\n"); - *cmdList = (*cmdList)->nextcommand; + *cmdList = CMD_node_next(*cmdList); } else { WINE_TRACE("Found end of this nested IF statement, ending this if\n"); break; } } else if (!processThese) { - if (curPosition == *cmdList) *cmdList = (*cmdList)->nextcommand; + if (curPosition == *cmdList) *cmdList = CMD_node_next(*cmdList); WINE_TRACE("Skipping this command, as in not process mode (next = %p)\n", *cmdList); } else { WINE_TRACE("Found end of this IF statement (next = %p)\n", *cmdList); @@ -2219,26 +2219,26 @@ void WCMD_for (WCHAR *p, CMD_NODE **cmdList) { }
/* Save away where the set of data starts and the variable */ - thisDepth = (*cmdList)->single->bracketDepth; - *cmdList = (*cmdList)->nextcommand; + thisDepth = CMD_node_get_depth(*cmdList); + *cmdList = CMD_node_next(*cmdList); setStart = (*cmdList);
/* Skip until the close bracket */ WINE_TRACE("Searching %p as the set\n", *cmdList); while (*cmdList && - (*cmdList)->single->command != NULL && - (*cmdList)->single->bracketDepth > thisDepth) { + CMD_node_get_command(*cmdList)->command != NULL && + CMD_node_get_depth(*cmdList) > thisDepth) { WINE_TRACE("Skipping %p which is part of the set\n", *cmdList); - *cmdList = (*cmdList)->nextcommand; + *cmdList = CMD_node_next(*cmdList); }
/* Skip the close bracket, if there is one */ - if (*cmdList) *cmdList = (*cmdList)->nextcommand; + if (*cmdList) *cmdList = CMD_node_next(*cmdList);
/* Syntax error if missing close bracket, or nothing following it and once we have the complete set, we expect a DO */ WINE_TRACE("Looking for 'do ' in %p\n", *cmdList); - if ((*cmdList == NULL) || !WCMD_keyword_ws_found(L"do", (*cmdList)->single->command)) { + if ((*cmdList == NULL) || !WCMD_keyword_ws_found(L"do", CMD_node_get_command(*cmdList)->command)) { WCMD_output_stderr (WCMD_LoadMessage(WCMD_SYNTAXERR)); return; } @@ -2252,7 +2252,7 @@ void WCMD_for (WCHAR *p, CMD_NODE **cmdList) { /* Save away the starting position for the commands (and offset for the first one) */ cmdStart = *cmdList; - firstCmd = (*cmdList)->single->command + 3; /* Skip 'do ' */ + firstCmd = CMD_node_get_command(*cmdList)->command + 3; /* Skip 'do ' */ itemNum = 0;
/* If we are recursing directories (ie /R), add all sub directories now, then @@ -2262,8 +2262,8 @@ void WCMD_for (WCHAR *p, CMD_NODE **cmdList) { thisSet = setStart; /* Loop through all set entries */ while (thisSet && - thisSet->single->command != NULL && - thisSet->single->bracketDepth >= thisDepth) { + CMD_node_get_command(thisSet)->command != NULL && + CMD_node_get_depth(thisSet) >= thisDepth) {
/* Loop through all entries on the same line */ WCHAR *staticitem; @@ -2272,7 +2272,7 @@ void WCMD_for (WCHAR *p, CMD_NODE **cmdList) {
WINE_TRACE("Processing for set %p\n", thisSet); i = 0; - while (*(staticitem = WCMD_parameter (thisSet->single->command, i, &itemStart, TRUE, FALSE))) { + while (*(staticitem = WCMD_parameter (CMD_node_get_command(thisSet)->command, i, &itemStart, TRUE, FALSE))) {
/* * If the parameter within the set has a wildcard then search for matching files @@ -2455,7 +2455,7 @@ void WCMD_for (WCHAR *p, CMD_NODE **cmdList) { }
/* Move onto the next set line */ - if (thisSet) thisSet = thisSet->nextcommand; + if (thisSet) thisSet = CMD_node_next(thisSet); }
/* If /L is provided, now run the for loop */ @@ -2516,7 +2516,7 @@ void WCMD_for (WCHAR *p, CMD_NODE **cmdList) { value needs to be the NEXT command to execute, which it either is, or we need to step over the closing bracket */ *cmdList = cmdEnd; - if (cmdEnd && cmdEnd->single->command == NULL) *cmdList = cmdEnd->nextcommand; + if (cmdEnd && CMD_node_get_command(cmdEnd)->command == NULL) *cmdList = CMD_node_next(cmdEnd); }
/************************************************************************** diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index 7dcea05e557..ce31192d5ae 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -40,7 +40,7 @@ typedef enum _CMDdelimiters { CMD_NONE, /* End of line or single & */ CMD_ONFAILURE, /* || */ CMD_ONSUCCESS, /* && */ - CMD_PIPE /* Single | */ + CMD_PIPE, /* Single | */ } CMD_DELIMITERS;
/* Data structure to hold commands to be processed */ @@ -60,6 +60,22 @@ typedef struct _CMD_NODE struct _CMD_NODE *nextcommand; /* Next command string to execute */ } CMD_NODE;
+/* temporary helpers to fake a list into a tree */ +/* Note: for binary op, left should be a CMD_SINGLE node */ +static inline CMD_COMMAND *CMD_node_get_command(const CMD_NODE *node) +{ + return node->single; +} +static inline CMD_NODE *CMD_node_next(const CMD_NODE *node) +{ + return node->nextcommand; +} +static inline int CMD_node_get_depth(const CMD_NODE *node) +{ + return node->single->bracketDepth; +} +/* end temporary */ + void WCMD_assoc (const WCHAR *, BOOL); void WCMD_batch (WCHAR *, WCHAR *, BOOL, WCHAR *, HANDLE); void WCMD_call (WCHAR *command); diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index abef0f7e5f4..ce7a32d79e0 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -1334,8 +1334,8 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects, process has to finish before the next one can start but this requires a change to not wait for the first app to finish but rather the pipe */ if (!(cmd_index == WCMD_FOR || cmd_index == WCMD_IF) && - cmdList && (*cmdList)->nextcommand && - (*cmdList)->nextcommand->single->prevDelim == CMD_PIPE) { + cmdList && CMD_node_next(*cmdList) && + CMD_node_get_command(CMD_node_next(*cmdList))->prevDelim == CMD_PIPE) {
WCHAR temp_path[MAX_PATH];
@@ -1345,14 +1345,14 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects,
/* Generate a unique temporary filename */ GetTempPathW(ARRAY_SIZE(temp_path), temp_path); - GetTempFileNameW(temp_path, L"CMD", 0, (*cmdList)->nextcommand->single->pipeFile); + GetTempFileNameW(temp_path, L"CMD", 0, CMD_node_get_command(CMD_node_next(*cmdList))->pipeFile); WINE_TRACE("Using temporary file of %s\n", - wine_dbgstr_w((*cmdList)->nextcommand->single->pipeFile)); + wine_dbgstr_w(CMD_node_get_command(CMD_node_next(*cmdList))->pipeFile)); }
/* If piped output, send stdout to the pipe by appending >filename to redirects */ if (piped) { - wsprintfW (new_redir, L"%s > %s", redirects, (*cmdList)->nextcommand->single->pipeFile); + wsprintfW (new_redir, L"%s > %s", redirects, CMD_node_get_command(CMD_node_next(*cmdList))->pipeFile); WINE_TRACE("Redirects now %s\n", wine_dbgstr_w(new_redir)); } else { lstrcpyW(new_redir, redirects); @@ -1404,9 +1404,9 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects, */ if (!(cmd_index == WCMD_FOR || cmd_index == WCMD_IF)) { /* STDIN could come from a preceding pipe, so delete on close if it does */ - if (cmdList && (*cmdList)->single->pipeFile[0] != 0x00) { - WINE_TRACE("Input coming from %s\n", wine_dbgstr_w((*cmdList)->single->pipeFile)); - h = CreateFileW((*cmdList)->single->pipeFile, GENERIC_READ, + if (cmdList && CMD_node_get_command(*cmdList)->pipeFile[0] != 0x00) { + WINE_TRACE("Input coming from %s\n", wine_dbgstr_w(CMD_node_get_command(*cmdList)->pipeFile)); + h = CreateFileW(CMD_node_get_command(*cmdList)->pipeFile, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL); if (h == INVALID_HANDLE_VALUE) { @@ -1418,7 +1418,7 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects, SetStdHandle (STD_INPUT_HANDLE, h);
/* No need to remember the temporary name any longer once opened */ - (*cmdList)->single->pipeFile[0] = 0x00; + CMD_node_get_command(*cmdList)->pipeFile[0] = 0x00;
/* Otherwise STDIN could come from a '<' redirect */ } else if ((pos = wcschr(new_redir,'<')) != NULL) { @@ -1681,12 +1681,12 @@ static void WCMD_DumpCommands(CMD_NODE *commands) { while (thisCmd != NULL) { WINE_TRACE("%p %d %2.2d %p %s Redir:%s\n", thisCmd, - thisCmd->single->prevDelim, - thisCmd->single->bracketDepth, - thisCmd->nextcommand, - wine_dbgstr_w(thisCmd->single->command), - wine_dbgstr_w(thisCmd->single->redirects)); - thisCmd = thisCmd->nextcommand; + CMD_node_get_command(thisCmd)->prevDelim, + CMD_node_get_depth(thisCmd), + CMD_node_next(thisCmd), + wine_dbgstr_w(CMD_node_get_command(thisCmd)->command), + wine_dbgstr_w(CMD_node_get_command(thisCmd)->redirects)); + thisCmd = CMD_node_next(thisCmd); } }
@@ -2369,7 +2369,7 @@ CMD_NODE *WCMD_process_commands(CMD_NODE *thisCmd, BOOL oneBracket,
int bdepth = -1;
- if (thisCmd && oneBracket) bdepth = thisCmd->single->bracketDepth; + if (thisCmd && oneBracket) bdepth = CMD_node_get_depth(thisCmd);
/* Loop through the commands, processing them one by one */ while (thisCmd) { @@ -2378,23 +2378,23 @@ CMD_NODE *WCMD_process_commands(CMD_NODE *thisCmd, BOOL oneBracket,
/* If processing one bracket only, and we find the end bracket entry (or less), return */ - if (oneBracket && !thisCmd->single->command && - bdepth <= thisCmd->single->bracketDepth) { + if (oneBracket && !CMD_node_get_command(thisCmd)->command && + bdepth <= CMD_node_get_depth(thisCmd)) { WINE_TRACE("Finished bracket @ %p, next command is %p\n", - thisCmd, thisCmd->nextcommand); - return thisCmd->nextcommand; + thisCmd, CMD_node_next(thisCmd)); + return CMD_node_next(thisCmd); }
/* Ignore the NULL entries a ')' inserts (Only 'if' cares about them and it will be handled in there) Also, skip over any batch labels (eg. :fred) */ - if (thisCmd->single->command && thisCmd->single->command[0] != ':') { - WINE_TRACE("Executing command: '%s'\n", wine_dbgstr_w(thisCmd->single->command)); - WCMD_execute (thisCmd->single->command, thisCmd->single->redirects, &thisCmd, retrycall); + if (CMD_node_get_command(thisCmd)->command && CMD_node_get_command(thisCmd)->command[0] != ':') { + WINE_TRACE("Executing command: '%s'\n", wine_dbgstr_w(CMD_node_get_command(thisCmd)->command)); + WCMD_execute (CMD_node_get_command(thisCmd)->command, CMD_node_get_command(thisCmd)->redirects, &thisCmd, retrycall); }
/* Step on unless the command itself already stepped on */ - if (thisCmd == origCmd) thisCmd = thisCmd->nextcommand; + if (thisCmd == origCmd) thisCmd = CMD_node_next(thisCmd); } return NULL; } @@ -2419,8 +2419,8 @@ void WCMD_free_commands(CMD_NODE *cmds) { /* Loop through the commands, freeing them one by one */ while (cmds) { CMD_NODE *thisCmd = cmds; - cmds = cmds->nextcommand; - WCMD_free_command(thisCmd->single); + cmds = CMD_node_next(cmds); + WCMD_free_command(CMD_node_get_command(thisCmd)); free(thisCmd); } }
From: Eric Pouech epouech@codeweavers.com
For now, the tree is degenerated into a list (as it was before).
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/builtins.c | 33 +++++++--- programs/cmd/wcmd.h | 37 +++++++---- programs/cmd/wcmdmain.c | 136 ++++++++++++++++++++++++++-------------- 3 files changed, 136 insertions(+), 70 deletions(-)
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index 4a012166e25..bc9ba9e5f11 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -1524,6 +1524,7 @@ static void WCMD_part_execute(CMD_NODE **cmdList, const WCHAR *firstcmd, { CMD_NODE *curPosition = *cmdList; int myDepth = CMD_node_get_depth(*cmdList); + CMD_OPERATOR prev_op = CMD_CONCAT;
WINE_TRACE("cmdList(%p), firstCmd(%s), doIt(%d), isIF(%d)\n", cmdList, wine_dbgstr_w(firstcmd), executecmds, isIF); @@ -1550,31 +1551,40 @@ static void WCMD_part_execute(CMD_NODE **cmdList, const WCHAR *firstcmd, /* execute all appropriate commands */ curPosition = *cmdList;
- WINE_TRACE("Processing cmdList(%p) - delim(%d) bd(%d / %d) processThese(%d)\n", + WINE_TRACE("Processing cmdList(%p) - operator(%d) bd(%d / %d) processThese(%d)\n", *cmdList, - CMD_node_get_command(*cmdList)->prevDelim, + prev_op, CMD_node_get_depth(*cmdList), myDepth, processThese);
/* Execute any statements appended to the line */ /* FIXME: Only if previous call worked for && or failed for || */ - if (CMD_node_get_command(*cmdList)->prevDelim == CMD_ONFAILURE || - CMD_node_get_command(*cmdList)->prevDelim == CMD_ONSUCCESS) { + if (prev_op == CMD_ONFAILURE || + prev_op == CMD_ONSUCCESS) { if (processThese && CMD_node_get_command(*cmdList)->command) { WCMD_execute (CMD_node_get_command(*cmdList)->command, CMD_node_get_command(*cmdList)->redirects, cmdList, FALSE); } - if (curPosition == *cmdList) *cmdList = CMD_node_next(*cmdList); + if (curPosition == *cmdList) + { + prev_op = (*cmdList)->op; + *cmdList = CMD_node_next(*cmdList); + }
/* Execute any appended to the statement with (...) */ } else if (CMD_node_get_depth(*cmdList) > myDepth) { if (processThese) { + /* FIXME this is wrong, we don't recompute prev_op */ *cmdList = WCMD_process_commands(*cmdList, TRUE, FALSE); } else { WINE_TRACE("Skipping command %p due to stack depth\n", *cmdList); } - if (curPosition == *cmdList) *cmdList = CMD_node_next(*cmdList); + if (curPosition == *cmdList) + { + prev_op = (*cmdList)->op; + *cmdList = CMD_node_next(*cmdList); + }
/* End of the command - does 'ELSE ' follow as the next command? */ } else { @@ -1597,15 +1607,20 @@ static void WCMD_part_execute(CMD_NODE **cmdList, const WCHAR *firstcmd, depth, including skipping commands and their subsequent pipes (eg cmd | prog) */ do { + prev_op = (*cmdList)->op; *cmdList = CMD_node_next(*cmdList); } while (*cmdList && (CMD_node_get_depth(*cmdList) > myDepth || - CMD_node_get_command(*cmdList)->prevDelim)); + (prev_op != CMD_SINGLE && prev_op != CMD_CONCAT)));
/* After the else is complete, we need to now process subsequent commands */ processThese = TRUE; } - if (curPosition == *cmdList) *cmdList = CMD_node_next(*cmdList); + if (curPosition == *cmdList) + { + prev_op = (*cmdList)->op; + *cmdList = CMD_node_next(*cmdList); + }
/* If we were in an IF statement and we didn't find an else and yet we get back to the same bracket depth as the IF, then the IF statement is over. This is required @@ -1613,6 +1628,7 @@ static void WCMD_part_execute(CMD_NODE **cmdList, const WCHAR *firstcmd, } else if (isIF && CMD_node_get_depth(*cmdList) == myDepth) { if (WCMD_keyword_ws_found(L"do", CMD_node_get_command(*cmdList)->command)) { WINE_TRACE("Still inside FOR-loop, not an end of IF statement\n"); + prev_op = (*cmdList)->op; *cmdList = CMD_node_next(*cmdList); } else { WINE_TRACE("Found end of this nested IF statement, ending this if\n"); @@ -1628,7 +1644,6 @@ static void WCMD_part_execute(CMD_NODE **cmdList, const WCHAR *firstcmd, } } } - return; }
static BOOL option_equals(WCHAR **haystack, const WCHAR *needle) diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index ce31192d5ae..e4e480294b0 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -36,12 +36,14 @@
/* Data structure to hold commands delimiters/separators */
-typedef enum _CMDdelimiters { - CMD_NONE, /* End of line or single & */ - CMD_ONFAILURE, /* || */ - CMD_ONSUCCESS, /* && */ - CMD_PIPE, /* Single | */ -} CMD_DELIMITERS; +typedef enum _CMD_OPERATOR +{ + CMD_SINGLE, /* single command */ + CMD_CONCAT, /* & */ + CMD_ONFAILURE, /* || */ + CMD_ONSUCCESS, /* && */ + CMD_PIPE, /* Single | */ +} CMD_OPERATOR;
/* Data structure to hold commands to be processed */
@@ -49,30 +51,39 @@ typedef struct _CMD_COMMAND { WCHAR *command; /* Command string to execute */ WCHAR *redirects; /* Redirects in place */ - CMD_DELIMITERS prevDelim; /* Previous delimiter */ int bracketDepth;/* How deep bracketing have we got to */ WCHAR pipeFile[MAX_PATH]; /* Where to get input from for pipes */ } CMD_COMMAND;
typedef struct _CMD_NODE { - CMD_COMMAND *single; - struct _CMD_NODE *nextcommand; /* Next command string to execute */ + CMD_OPERATOR op; /* operator */ + union + { + CMD_COMMAND *command; /* CMD_SINGLE */ + struct /* binary operator (CMD_CONCAT, ONFAILURE, ONSUCCESS, PIPE) */ + { + struct _CMD_NODE *left; + struct _CMD_NODE *right; + }; + }; } CMD_NODE; - /* temporary helpers to fake a list into a tree */ /* Note: for binary op, left should be a CMD_SINGLE node */ static inline CMD_COMMAND *CMD_node_get_command(const CMD_NODE *node) { - return node->single; + if (node->op == CMD_SINGLE) return node->command; + /* assert(node->left && node->left->op == CMD_SINGLE); */ + return node->left->command; } static inline CMD_NODE *CMD_node_next(const CMD_NODE *node) { - return node->nextcommand; + return (node->op == CMD_SINGLE) ? NULL : node->right; } static inline int CMD_node_get_depth(const CMD_NODE *node) { - return node->single->bracketDepth; + CMD_COMMAND *cmd = CMD_node_get_command(node); + return cmd->bracketDepth; } /* end temporary */
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index ce7a32d79e0..2c5cf4a3196 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -942,6 +942,31 @@ static void WCMD_parse (const WCHAR *s, WCHAR *q, WCHAR *p1, WCHAR *p2) } }
+/* ============================== */ +/* Data structures for commands */ +/* ============================== */ + +static CMD_NODE *node_create_single(CMD_COMMAND *c) +{ + CMD_NODE *new = xalloc(sizeof(CMD_NODE)); + + new->op = CMD_SINGLE; + new->command = c; + + return new; +} + +static CMD_NODE *node_create_binary(CMD_OPERATOR op, CMD_NODE *l, CMD_NODE *r) +{ + CMD_NODE *new = xalloc(sizeof(CMD_NODE)); + + new->op = op; + new->left = l; + new->right = r; + + return new; +} + static void init_msvcrt_io_block(STARTUPINFOW* st) { STARTUPINFOW st_p; @@ -1334,8 +1359,7 @@ void WCMD_execute (const WCHAR *command, const WCHAR *redirects, process has to finish before the next one can start but this requires a change to not wait for the first app to finish but rather the pipe */ if (!(cmd_index == WCMD_FOR || cmd_index == WCMD_IF) && - cmdList && CMD_node_next(*cmdList) && - CMD_node_get_command(CMD_node_next(*cmdList))->prevDelim == CMD_PIPE) { + cmdList && (*cmdList)->op == CMD_PIPE) {
WCHAR temp_path[MAX_PATH];
@@ -1669,24 +1693,33 @@ WCHAR *WCMD_LoadMessage(UINT id) { return msg; }
+static const char *op2str(CMD_OPERATOR op) +{ + static const char* optable[] = {"op-single", "op-&", "op-||", "op-&&", "op-|"}; + if (op < ARRAY_SIZE(optable)) return optable[op]; + return "op-unk"; +} + /*************************************************************************** * WCMD_DumpCommands * * Dumps out the parsed command line to ensure syntax is correct */ -static void WCMD_DumpCommands(CMD_NODE *commands) { +static void WCMD_DumpCommands(CMD_NODE *commands) +{ CMD_NODE *thisCmd = commands;
WINE_TRACE("Parsed line:\n"); - while (thisCmd != NULL) { - WINE_TRACE("%p %d %2.2d %p %s Redir:%s\n", - thisCmd, - CMD_node_get_command(thisCmd)->prevDelim, - CMD_node_get_depth(thisCmd), - CMD_node_next(thisCmd), - wine_dbgstr_w(CMD_node_get_command(thisCmd)->command), - wine_dbgstr_w(CMD_node_get_command(thisCmd)->redirects)); - thisCmd = CMD_node_next(thisCmd); + while (thisCmd != NULL) + { + TRACE("%p %2.2d %p %s Redir:%s %s\n", + thisCmd, + CMD_node_get_depth(thisCmd), + CMD_node_next(thisCmd), + wine_dbgstr_w(CMD_node_get_command(thisCmd)->command), + wine_dbgstr_w(CMD_node_get_command(thisCmd)->redirects), + op2str(thisCmd->op)); + thisCmd = CMD_node_next(thisCmd); } }
@@ -1698,8 +1731,8 @@ static void WCMD_DumpCommands(CMD_NODE *commands) { static CMD_COMMAND *WCMD_createCommand(WCHAR *command, int *commandLen, WCHAR *redirs, int *redirLen, WCHAR **copyTo, int **copyToLen, - CMD_DELIMITERS prevDelim, int curDepth) - { + int curDepth) +{ CMD_COMMAND *thisEntry = NULL;
/* Allocate storage for command */ @@ -1730,21 +1763,24 @@ static CMD_COMMAND *WCMD_createCommand(WCHAR *command, int *commandLen, }
/* Fill in other fields */ - thisEntry->prevDelim = prevDelim; thisEntry->bracketDepth = curDepth; return thisEntry; }
-static void WCMD_appendCommand(CMD_COMMAND *command, CMD_NODE **node) +static void WCMD_appendCommand(CMD_OPERATOR op, CMD_COMMAND *command, CMD_NODE **node) { - CMD_NODE *new = xalloc(sizeof(CMD_NODE)); + /* append as left to right operators */ + if (*node) + { + CMD_NODE **last = node; + while ((*last)->op != CMD_SINGLE) + last = &(*last)->right;
- if (new) + *last = node_create_binary(op, *last, node_create_single(command)); + } + else { - new->single = command; - new->nextcommand = NULL; - for (; *node; node = &((*node)->nextcommand)) {} - *node = new; + *node = node_create_single(command); } }
@@ -1828,7 +1864,7 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE int *curLen; int curDepth = 0; CMD_COMMAND *single_cmd = NULL; - CMD_DELIMITERS prevDelim = CMD_NONE; + CMD_OPERATOR cmd_op = CMD_CONCAT; static WCHAR *extraSpace = NULL; /* Deliberately never freed */ BOOL inOneLine = FALSE; BOOL inFor = FALSE; @@ -2099,15 +2135,15 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE single_cmd = WCMD_createCommand(curString, &curStringLen, curRedirs, &curRedirsLen, &curCopyTo, &curLen, - prevDelim, curDepth); - WCMD_appendCommand(single_cmd, output); + curDepth); + WCMD_appendCommand(cmd_op, single_cmd, output); }
if (*(curPos+1) == '|') { curPos++; /* Skip other | */ - prevDelim = CMD_ONFAILURE; + cmd_op = CMD_ONFAILURE; } else { - prevDelim = CMD_PIPE; + cmd_op = CMD_PIPE; }
/* If in an IF or ELSE statement, put subsequent chained @@ -2172,8 +2208,8 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE single_cmd = WCMD_createCommand(curString, &curStringLen, curRedirs, &curRedirsLen, &curCopyTo, &curLen, - prevDelim, curDepth); - WCMD_appendCommand(single_cmd, output); + curDepth); + WCMD_appendCommand(cmd_op, single_cmd, output);
curDepth++; } else { @@ -2203,15 +2239,15 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE single_cmd = WCMD_createCommand(curString, &curStringLen, curRedirs, &curRedirsLen, &curCopyTo, &curLen, - prevDelim, curDepth); - WCMD_appendCommand(single_cmd, output); + curDepth); + WCMD_appendCommand(cmd_op, single_cmd, output); }
if (*(curPos+1) == '&') { curPos++; /* Skip other & */ - prevDelim = CMD_ONSUCCESS; + cmd_op = CMD_ONSUCCESS; } else { - prevDelim = CMD_NONE; + cmd_op = CMD_CONCAT; } /* If in an IF or ELSE statement, put subsequent chained commands at a higher depth as if brackets were supplied @@ -2235,17 +2271,17 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE single_cmd = WCMD_createCommand(curString, &curStringLen, curRedirs, &curRedirsLen, &curCopyTo, &curLen, - prevDelim, curDepth); - WCMD_appendCommand(single_cmd, output); + curDepth); + WCMD_appendCommand(cmd_op, single_cmd, output); }
/* Add an empty entry to the command list */ - prevDelim = CMD_NONE; + cmd_op = CMD_CONCAT; single_cmd = WCMD_createCommand(NULL, &curStringLen, curRedirs, &curRedirsLen, &curCopyTo, &curLen, - prevDelim, curDepth); - WCMD_appendCommand(single_cmd, output); + curDepth); + WCMD_appendCommand(cmd_op, single_cmd, output);
curDepth--;
@@ -2281,8 +2317,8 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE single_cmd = WCMD_createCommand(curString, &curStringLen, curRedirs, &curRedirsLen, &curCopyTo, &curLen, - prevDelim, curDepth); - WCMD_appendCommand(single_cmd, output); + curDepth); + WCMD_appendCommand(cmd_op, single_cmd, output);
/* If we had a single line if or else, and we pretended to add brackets, end them now */ @@ -2302,7 +2338,7 @@ WCHAR *WCMD_ReadAndParseLine(const WCHAR *optionalcmd, CMD_NODE **output, HANDLE WINE_TRACE("Need to read more data as outstanding brackets or carets\n"); inOneLine = FALSE; ignoreBracket = FALSE; - prevDelim = CMD_NONE; + cmd_op = CMD_CONCAT; inQuotes = 0; memset(extraSpace, 0x00, (MAXSTRING+1) * sizeof(WCHAR)); extraData = extraSpace; @@ -2414,14 +2450,18 @@ static void WCMD_free_command(CMD_COMMAND *cmd) * pointer will be modified within the commands, and hence a single free * routine is simpler */ -void WCMD_free_commands(CMD_NODE *cmds) { - +void WCMD_free_commands(CMD_NODE *cmds) +{ /* Loop through the commands, freeing them one by one */ - while (cmds) { - CMD_NODE *thisCmd = cmds; - cmds = CMD_node_next(cmds); - WCMD_free_command(CMD_node_get_command(thisCmd)); - free(thisCmd); + while (cmds) + { + CMD_NODE *thisCmd = cmds; + cmds = CMD_node_next(cmds); + if (thisCmd->op == CMD_SINGLE) + WCMD_free_command(thisCmd->command); + else + WCMD_free_commands(thisCmd->left); + free(thisCmd); } }