This is part X of cmd engine rewrite.
This serie: - tackles some other variable expansion issues, - attaches redirections to CMD_NODE (where it belongs) (eg. "> foo (IF 1==1 echo a)" is a valid command), - preparing for next serie.
Next serie will activate he updated lexer and parser.
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/tests/test_builtins.cmd | 21 +++++++++++++++++++++ programs/cmd/tests/test_builtins.cmd.exp | 6 ++++++ 2 files changed, 27 insertions(+)
diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd index d5397560bc6..ebcec489b7c 100644 --- a/programs/cmd/tests/test_builtins.cmd +++ b/programs/cmd/tests/test_builtins.cmd @@ -2785,6 +2785,27 @@ call if 1==1 ( echo ... and else! ) call call call echo passed +set WINE_FOO=WINE_BAR +set WINE_BAR=abc +call echo %%%WINE_FOO%%% +call cmd.exe /c echo %%%WINE_FOO%%% +call echo %%%%%WINE_FOO%%%%% +call cmd.exe /c echo %%%%%WINE_FOO%%%%% + +set WINE_BAR=abc +set WINE_FOO=%%WINE_BAR%% + +call :call_expand %WINE_FOO% %%WINE_FOO%% %%%WINE_FOO%%% +goto :call_expand_done + +:call_expand +set WINE_BAR=def +echo %1 %2 %3 +call echo %1 %2 %3 +exit /b 0 + +:call_expand_done + cd .. & rd /s/q foobar
echo --- mixing batch and builtins diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 0102c3bcbc5..146dbb5ad0a 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -1585,6 +1585,12 @@ Line two Get if ... and else! passed +abc +abc +%WINE_BAR% +@todo_wine@abc +@todo_wine@abc %WINE_BAR% %WINE_BAR% +@todo_wine@abc def def --- mixing batch and builtins bar@space@ @todo_wine@foo
From: Eric Pouech epouech@codeweavers.com
Fix several incorrect behaviors that (partly) compensate each others: - when replacing %XXX% by %YYY%, advance current position after the copied %YYY% (otherwise it will expand %YYY% in same run), - don't parse the command in run_program(), - always expand full content in CALL command.
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/batch.c | 78 ++++++++++---------- programs/cmd/tests/test_builtins.cmd.exp | 6 +- programs/cmd/tests/test_cmdline.cmd.exp | 2 +- programs/cmd/wcmd.h | 4 +- programs/cmd/wcmdmain.c | 90 ++++++++++++------------ 5 files changed, 88 insertions(+), 92 deletions(-)
diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c index 402a6f649d2..fe859dd52c4 100644 --- a/programs/cmd/batch.c +++ b/programs/cmd/batch.c @@ -42,7 +42,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(cmd); * a label to goto once opened. */
-void WCMD_batch (WCHAR *file, WCHAR *command, BOOL called, WCHAR *startLabel, HANDLE pgmHandle) +void WCMD_batch (WCHAR *file, WCHAR *command, WCHAR *startLabel, HANDLE pgmHandle) { HANDLE h = INVALID_HANDLE_VALUE; BATCH_CONTEXT *prev_context; @@ -112,11 +112,6 @@ void WCMD_batch (WCHAR *file, WCHAR *command, BOOL called, WCHAR *startLabel, HA
free(context->batchfileW); LocalFree(context); - if ((prev_context != NULL) && (!called)) { - WINE_TRACE("Batch completed, but was not 'called' so skipping outer batch too\n"); - prev_context -> skip_rest = TRUE; - context = prev_context; - } context = prev_context; }
@@ -642,46 +637,47 @@ void WCMD_HandleTildeModifiers(WCHAR **start, BOOL atExecute) WCMD_strsubstW(*start, lastModifier+1, finaloutput, -1); }
+extern void WCMD_expand(const WCHAR *, WCHAR *); + /******************************************************************* * WCMD_call - processes a batch call statement * * If there is a leading ':', calls within this batch program * otherwise launches another program. */ -void WCMD_call (WCHAR *command) { - - /* Run other program if no leading ':' */ - if (*command != ':') { - WCMD_run_program(command, TRUE); - /* If the thing we try to run does not exist, call returns 1 */ - if (errorlevel) errorlevel = ERROR_INVALID_FUNCTION; - } else { - - WCHAR gotoLabel[MAX_PATH]; - - lstrcpyW(gotoLabel, param1); - - if (context) { - - LARGE_INTEGER li; - - /* Save the for variable context, then start with an empty context - as for loop variables do not survive a call */ - WCMD_save_for_loop_context(TRUE); - - /* Save the current file position, call the same file, - restore position */ - li.QuadPart = 0; - li.u.LowPart = SetFilePointer(context -> h, li.u.LowPart, - &li.u.HighPart, FILE_CURRENT); - WCMD_batch (context->batchfileW, command, TRUE, gotoLabel, context->h); - SetFilePointer(context -> h, li.u.LowPart, - &li.u.HighPart, FILE_BEGIN); - - /* Restore the for loop context */ - WCMD_restore_for_loop_context(); - } else { - WCMD_output_asis_stderr(WCMD_LoadMessage(WCMD_CALLINSCRIPT)); +void WCMD_call(WCHAR *command) +{ + WCHAR buffer[MAXSTRING]; + WCMD_expand(command, buffer); + + /* Run other program if no leading ':' */ + if (*command != ':') + { + WCMD_run_program(buffer, TRUE); + /* If the thing we try to run does not exist, call returns 1 */ + if (errorlevel) errorlevel = ERROR_INVALID_FUNCTION; } - } + else if (context) + { + LARGE_INTEGER li; + WCHAR gotoLabel[MAX_PATH]; + + lstrcpyW(gotoLabel, param1); + + /* Save the for variable context, then start with an empty context + as for loop variables do not survive a call */ + WCMD_save_for_loop_context(TRUE); + + /* Save the current file position, call the same file, + restore position */ + li.QuadPart = 0; + li.u.LowPart = SetFilePointer(context->h, li.u.LowPart, + &li.u.HighPart, FILE_CURRENT); + WCMD_batch(context->batchfileW, buffer, gotoLabel, context->h); + SetFilePointer(context->h, li.u.LowPart, &li.u.HighPart, FILE_BEGIN); + + /* Restore the for loop context */ + WCMD_restore_for_loop_context(); + } else + WCMD_output_asis_stderr(WCMD_LoadMessage(WCMD_CALLINSCRIPT)); } diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 146dbb5ad0a..31ed5b92765 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -1588,9 +1588,9 @@ passed abc abc %WINE_BAR% -@todo_wine@abc -@todo_wine@abc %WINE_BAR% %WINE_BAR% -@todo_wine@abc def def +abc +abc %WINE_BAR% %WINE_BAR% +abc def def --- mixing batch and builtins bar@space@ @todo_wine@foo diff --git a/programs/cmd/tests/test_cmdline.cmd.exp b/programs/cmd/tests/test_cmdline.cmd.exp index afd4a4eefdf..58cd5480292 100644 --- a/programs/cmd/tests/test_cmdline.cmd.exp +++ b/programs/cmd/tests/test_cmdline.cmd.exp @@ -115,7 +115,7 @@ bar@space@ 0@space@ !@space@ 0@space@@or_broken@!@space@ -@todo_wine@0@space@ +0@space@ '@space@ +@space@ 0@space@ diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index e2ee0fc02ac..19d82c0f062 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -182,7 +182,7 @@ typedef int RETURN_CODE; #define RETURN_CODE_ABORTED (-999999)
void WCMD_assoc (const WCHAR *, BOOL); -void WCMD_batch (WCHAR *, WCHAR *, BOOL, WCHAR *, HANDLE); +void WCMD_batch(WCHAR *, WCHAR *, WCHAR *, HANDLE); void WCMD_call (WCHAR *command); void WCMD_change_tty (void); void WCMD_choice (const WCHAR *); @@ -242,7 +242,7 @@ void WCMD_HandleTildeModifiers(WCHAR **start, BOOL atExecute);
WCHAR *WCMD_strip_quotes(WCHAR *cmd); WCHAR *WCMD_LoadMessage(UINT id); -void WCMD_strsubstW(WCHAR *start, const WCHAR* next, const WCHAR* insert, int len); +WCHAR *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_NODE **output, HANDLE readFrom); diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 1fe519aa149..f921f771b52 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -438,7 +438,7 @@ void *xrealloc(void *ptr, size_t size) * It's up to the caller to ensure there is enough space in the * destination buffer. */ -void WCMD_strsubstW(WCHAR *start, const WCHAR *next, const WCHAR *insert, int len) { +WCHAR *WCMD_strsubstW(WCHAR *start, const WCHAR *next, const WCHAR *insert, int len) {
if (len < 0) len=insert ? lstrlenW(insert) : 0; @@ -446,6 +446,7 @@ void WCMD_strsubstW(WCHAR *start, const WCHAR *next, const WCHAR *insert, int le memmove(start+len, next, (lstrlenW(next) + 1) * sizeof(*next)); if (insert) memcpy(start, insert, len * sizeof(*insert)); + return start + len; }
BOOL WCMD_get_fullpath(const WCHAR* in, SIZE_T outsize, WCHAR* out, WCHAR** start) @@ -568,8 +569,7 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start, WCHAR startchar) /* In batch program, missing terminator for % and no following ':' just removes the '%' */ if (context) { - WCMD_strsubstW(start, start + 1, NULL, 0); - return start; + return WCMD_strsubstW(start, start + 1, NULL, 0); } else {
/* In command processing, just ignore it - allows command line @@ -643,30 +643,21 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start, WCHAR startchar) /* Command line - just ignore this */ if (context == NULL) return endOfVar+1;
- /* Batch - replace unknown env var with nothing */ - if (colonpos == NULL) { - WCMD_strsubstW(start, endOfVar + 1, NULL, 0); - } else { - len = lstrlenW(thisVar); - thisVar[len-1] = 0x00; - /* If %:...% supplied, : is retained */ - if (colonpos == thisVar+1) { - WCMD_strsubstW(start, endOfVar + 1, colonpos, -1); - } else { - WCMD_strsubstW(start, endOfVar + 1, colonpos + 1, -1); - } - } - return start; - + if (colonpos == NULL) + return WCMD_strsubstW(start, endOfVar + 1, NULL, 0); + len = lstrlenW(thisVar); + thisVar[len-1] = 0x00; + /* If %:...% supplied, : is retained */ + if (colonpos == thisVar+1) + return WCMD_strsubstW(start, endOfVar + 1, colonpos, -1); + return WCMD_strsubstW(start, endOfVar + 1, colonpos + 1, -1); }
/* See if we need to do complex substitution (any ':'s), if not then our work here is done */ - if (colonpos == NULL) { - WCMD_strsubstW(start, endOfVar + 1, thisVarContents, -1); - return start; - } + if (colonpos == NULL) + return WCMD_strsubstW(start, endOfVar + 1, thisVarContents, -1);
/* Restore complex bit */ *colonpos = ':'; @@ -699,20 +690,18 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start, WCHAR startchar) startCopy = &thisVarContents[max(0, len + substrposition)]; }
- if (commapos == NULL) { + if (commapos == NULL) /* Copy the lot */ - WCMD_strsubstW(start, endOfVar + 1, startCopy, -1); - } else if (substrlength < 0) { + return WCMD_strsubstW(start, endOfVar + 1, startCopy, -1); + if (substrlength < 0) {
int copybytes = len + substrlength - (startCopy - thisVarContents); if (copybytes >= len) copybytes = len - 1; else if (copybytes < 0) copybytes = 0; - WCMD_strsubstW(start, endOfVar + 1, startCopy, copybytes); - } else { - substrlength = min(substrlength, len - (startCopy - thisVarContents)); - WCMD_strsubstW(start, endOfVar + 1, startCopy, substrlength); + return WCMD_strsubstW(start, endOfVar + 1, startCopy, copybytes); } - + substrlength = min(substrlength, len - (startCopy - thisVarContents)); + return WCMD_strsubstW(start, endOfVar + 1, startCopy, substrlength); /* search and replace manipulation */ } else { WCHAR *equalspos = wcsstr(colonpos, L"="); @@ -720,6 +709,7 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start, WCHAR startchar) WCHAR *found = NULL; WCHAR *searchIn; WCHAR *searchFor; + WCHAR *ret;
if (equalspos == NULL) return start+1; s = xstrdupW(endOfVar + 1); @@ -743,13 +733,14 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start, WCHAR startchar) /* Do replacement */ lstrcpyW(start, replacewith); lstrcatW(start, thisVarContents + (found-searchIn) + lstrlenW(searchFor+1)); + ret = start + wcslen(start); lstrcatW(start, s); } else { /* Copy as is */ lstrcpyW(start, thisVarContents); + ret = start + wcslen(start); lstrcatW(start, s); } - } else { /* Loop replacing all instances */ WCHAR *lastFound = searchIn; @@ -767,13 +758,14 @@ static WCHAR *WCMD_expand_envvar(WCHAR *start, WCHAR startchar) } lstrcatW(outputposn, thisVarContents + (lastFound-searchIn)); + ret = outputposn + wcslen(outputposn); lstrcatW(outputposn, s); } free(s); free(searchIn); free(searchFor); + return ret; } - return start; }
/***************************************************************************** @@ -831,8 +823,9 @@ static void handleExpansion(WCHAR *cmd, BOOL atExecute) { if (!atExecute && *(p+1) == startchar) { if (context) { WCMD_strsubstW(p, p+1, NULL, 0); + p++; } - p+=1; + else p+=2;
/* Replace %~ modifications if in batch program */ } else if (*(p+1) == '~') { @@ -843,7 +836,7 @@ static void handleExpansion(WCHAR *cmd, BOOL atExecute) { } else if (!atExecute && context && (i >= 0) && (i <= 9) && startchar == '%') { t = WCMD_parameter(context -> command, i + context -> shift_count[i], NULL, TRUE, TRUE); - WCMD_strsubstW(p, p+2, t, -1); + p = WCMD_strsubstW(p, p+2, t, -1);
/* Replace use of %* if in batch program*/ } else if (!atExecute && context && *(p+1)=='*' && startchar == '%') { @@ -852,15 +845,15 @@ static void handleExpansion(WCHAR *cmd, BOOL atExecute) { if (startOfParms != NULL) { startOfParms += lstrlenW(thisParm); while (*startOfParms==' ' || *startOfParms == '\t') startOfParms++; - WCMD_strsubstW(p, p+2, startOfParms, -1); + p = WCMD_strsubstW(p, p+2, startOfParms, -1); } else - WCMD_strsubstW(p, p+2, NULL, 0); + p = WCMD_strsubstW(p, p+2, NULL, 0);
} else { int forvaridx = for_var_char_to_index(*(p+1)); if (startchar == '%' && forvaridx != -1 && forloopcontext->variable[forvaridx]) { /* Replace the 2 characters, % and for variable character */ - WCMD_strsubstW(p, p + 2, forloopcontext->variable[forvaridx], -1); + p = WCMD_strsubstW(p, p + 2, forloopcontext->variable[forvaridx], -1); } else if (!atExecute || startchar == '!') { p = WCMD_expand_envvar(p, startchar);
@@ -930,6 +923,13 @@ static void WCMD_parse (const WCHAR *s, WCHAR *q, WCHAR *p1, WCHAR *p2) } }
+void WCMD_expand(const WCHAR *src, WCHAR *dst) +{ + wcscpy(dst, src); + handleExpansion(dst, FALSE); + WCMD_parse(dst, quals, param1, param2); +} + /* ============================== */ /* Data structures for commands */ /* ============================== */ @@ -1317,6 +1317,8 @@ static void init_msvcrt_io_block(STARTUPINFOW* st) } }
+static void execute_single_command(const WCHAR *command, CMD_NODE **cmdList, BOOL iscalled); + /****************************************************************************** * WCMD_run_program * @@ -1524,8 +1526,12 @@ void WCMD_run_program (WCHAR *command, BOOL called) if (ext && (!wcsicmp(ext, L".bat") || !wcsicmp(ext, L".cmd"))) { BOOL oldinteractive = interactive; interactive = FALSE; - WCMD_batch (thisDir, command, called, NULL, INVALID_HANDLE_VALUE); + WCMD_batch(thisDir, command, NULL, INVALID_HANDLE_VALUE); interactive = oldinteractive; + if (context && !called) { + TRACE("Batch completed, but was not 'called' so skipping outer batch too\n"); + context->skip_rest = TRUE; + } return; } else { DWORD exit_code; @@ -1574,13 +1580,7 @@ void WCMD_run_program (WCHAR *command, BOOL called)
/* Not found anywhere - were we called? */ if (called) { - 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); - WCMD_process_commands(toExecute, FALSE, called); - node_dispose_tree(toExecute); - toExecute = NULL; + execute_single_command(command, NULL, TRUE); return; }
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/builtins.c | 6 ++-- programs/cmd/wcmd.h | 12 ++++--- programs/cmd/wcmdmain.c | 71 +++++++++++++++++++++++++++++++---------- 3 files changed, 65 insertions(+), 24 deletions(-)
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index 1c39c35ca78..a39de313986 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -1562,7 +1562,7 @@ 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, CMD_node_get_command(*cmdList)->redirects, cmdList, FALSE); + WCMD_execute (firstcmd, CMD_node_get_single_node(*cmdList)->redirects, cmdList, FALSE); free(command); }
@@ -1590,7 +1590,7 @@ void WCMD_part_execute(CMD_NODE **cmdList, const WCHAR *firstcmd, 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, + WCMD_execute (CMD_node_get_command(*cmdList)->command, CMD_node_get_single_node(*cmdList)->redirects, cmdList, FALSE); } if (curPosition == *cmdList) @@ -1627,7 +1627,7 @@ void WCMD_part_execute(CMD_NODE **cmdList, const WCHAR *firstcmd, /* Skip leading whitespace between condition and the command */ while (*cmd && (*cmd==' ' || *cmd=='\t')) cmd++; if (*cmd) { - WCMD_execute (cmd, CMD_node_get_command(*cmdList)->redirects, cmdList, FALSE); + WCMD_execute (cmd, CMD_node_get_single_node(*cmdList)->redirects, cmdList, FALSE); } } else { /* Loop skipping all commands until we get back to the current diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index 19d82c0f062..6230646bce6 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -109,7 +109,6 @@ typedef struct _CMD_FOR_CONTROL typedef struct _CMD_COMMAND { WCHAR *command; /* Command string to execute */ - CMD_REDIRECTION *redirects; /* Redirects in place */ int bracketDepth;/* How deep bracketing have we got to */ WCHAR pipeFile[MAX_PATH]; /* Where to get input from for pipes */ } CMD_COMMAND; @@ -117,6 +116,7 @@ typedef struct _CMD_COMMAND typedef struct _CMD_NODE { CMD_OPERATOR op; /* operator */ + CMD_REDIRECTION *redirects; /* Redirects in place */ union { CMD_COMMAND *command; /* CMD_SINGLE */ @@ -129,11 +129,15 @@ typedef struct _CMD_NODE } 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) +static inline const CMD_NODE *CMD_node_get_single_node(const CMD_NODE *node) { - if (node->op == CMD_SINGLE) return node->command; /* assert(node->left && node->left->op == CMD_SINGLE); */ - return node->left->command; + return (node->op == CMD_SINGLE) ? node : node->left; +} + +static inline CMD_COMMAND *CMD_node_get_command(const CMD_NODE *node) +{ + return CMD_node_get_single_node(node)->command; } static inline CMD_NODE *CMD_node_next(const CMD_NODE *node) { diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index f921f771b52..13ed0e09bab 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -969,12 +969,28 @@ static CMD_REDIRECTION *redirection_create_clone(unsigned fd, unsigned fd_clone) return redir; }
+static const char *debugstr_redirection(const CMD_REDIRECTION *redir) +{ + switch (redir->kind) + { + case REDIR_READ_FROM: + return wine_dbg_sprintf("%u< (%ls)", redir->fd, redir->file); + case REDIR_WRITE_TO: + return wine_dbg_sprintf("%u> (%ls)", redir->fd, redir->file); + case REDIR_WRITE_APPEND: + return wine_dbg_sprintf("%u>> (%ls)", redir->fd, redir->file); + case REDIR_WRITE_CLONE: + return wine_dbg_sprintf("%u>&%u", redir->fd, redir->clone); + default: + return "-^-"; + } +} + static void command_dispose(CMD_COMMAND *cmd) { if (cmd) { free(cmd->command); - redirection_dispose_list(cmd->redirects); free(cmd); } } @@ -1098,19 +1114,23 @@ void node_dispose_tree(CMD_NODE *cmds) CMD_NODE *thisCmd = cmds; cmds = CMD_node_next(cmds); if (thisCmd->op == CMD_SINGLE) + { + redirection_dispose_list(thisCmd->redirects); command_dispose(thisCmd->command); + } else node_dispose_tree(thisCmd->left); free(thisCmd); } }
-static CMD_NODE *node_create_single(CMD_COMMAND *c) +static CMD_NODE *node_create_single(CMD_COMMAND *c, CMD_REDIRECTION *redir) { CMD_NODE *new = xalloc(sizeof(CMD_NODE));
new->op = CMD_SINGLE; new->command = c; + new->redirects = redir;
return new; } @@ -1122,6 +1142,7 @@ static CMD_NODE *node_create_binary(CMD_OPERATOR op, CMD_NODE *l, CMD_NODE *r) new->op = op; new->left = l; new->right = r; + new->redirects = NULL;
return new; } @@ -2269,6 +2290,7 @@ syntax_error: union token_parameter { CMD_COMMAND *command; + CMD_REDIRECTION *redirection; void *none; };
@@ -2280,7 +2302,7 @@ struct node_builder { enum builder_token { - TKN_EOL, TKN_AMP, TKN_BARBAR, TKN_AMPAMP, TKN_BAR, TKN_COMMAND, + TKN_EOL, TKN_REDIRECTION, TKN_AMP, TKN_BARBAR, TKN_AMPAMP, TKN_BAR, TKN_COMMAND, } token; union token_parameter parameter; } *stack; @@ -2290,13 +2312,14 @@ struct node_builder
static const char* debugstr_token(enum builder_token tkn, union token_parameter tkn_pmt) { - static const char *tokens[] = {"EOL", "&", "||", "&&", "|", "CMD"}; + static const char *tokens[] = {"EOL", "REDIR", "&", "||", "&&", "|", "CMD"};
if (tkn >= ARRAY_SIZE(tokens)) return "<<<>>>"; switch (tkn) { - case TKN_COMMAND: return wine_dbg_sprintf("%s {{%ls}}", tokens[tkn], tkn_pmt.command->command); - default: return wine_dbg_sprintf("%s", tokens[tkn]); + case TKN_COMMAND: return wine_dbg_sprintf("%s {{%ls}}", tokens[tkn], tkn_pmt.command ? tkn_pmt.command->command : L"<<nul>>"); + case TKN_REDIRECTION: return wine_dbg_sprintf("%s {{%s}}", tokens[tkn], debugstr_redirection(tkn_pmt.redirection)); + default: return wine_dbg_sprintf("%s", tokens[tkn]); } }
@@ -2353,7 +2376,7 @@ static void node_builder_consume(struct node_builder *builder) builder->pos++; }
-static void WCMD_appendCommand(CMD_OPERATOR op, CMD_COMMAND *command, CMD_NODE **node) +static void WCMD_appendCommand(CMD_OPERATOR op, CMD_COMMAND *command, CMD_REDIRECTION *redir, CMD_NODE **node) { /* append as left to right operators */ if (*node) @@ -2362,16 +2385,17 @@ static void WCMD_appendCommand(CMD_OPERATOR op, CMD_COMMAND *command, CMD_NODE * while ((*last)->op != CMD_SINGLE) last = &(*last)->right;
- *last = node_create_binary(op, *last, node_create_single(command)); + *last = node_create_binary(op, *last, node_create_single(command, redir)); } else { - *node = node_create_single(command); + *node = node_create_single(command, redir); } }
static BOOL node_builder_parse(struct node_builder *builder, CMD_NODE **result) { + CMD_REDIRECTION *redir; unsigned bogus_line; CMD_NODE *left = NULL; union token_parameter pmt; @@ -2383,6 +2407,7 @@ static BOOL node_builder_parse(struct node_builder *builder, CMD_NODE **result) for (;;) { CMD_OPERATOR cmd_op; + CMD_COMMAND *cmd;
done = FALSE; /* we always get a pair of OP CMMAND */ @@ -2400,11 +2425,25 @@ static BOOL node_builder_parse(struct node_builder *builder, CMD_NODE **result) if (done) break; node_builder_consume(builder);
- tkn = node_builder_peek_next_token(builder, &pmt); + if ((tkn = node_builder_peek_next_token(builder, &pmt)) == TKN_REDIRECTION) + { + redir = pmt.redirection; + node_builder_consume(builder); + tkn = node_builder_peek_next_token(builder, &pmt); + } + else redir = NULL; ERROR_IF(tkn != TKN_COMMAND) + cmd = pmt.command; node_builder_consume(builder);
- WCMD_appendCommand(cmd_op, pmt.command, &left); + if ((tkn = node_builder_peek_next_token(builder, &pmt)) == TKN_REDIRECTION) + { + CMD_REDIRECTION **p = &redir; + for (; *p; p = &(*p)->next) {} + *p = pmt.redirection; + node_builder_consume(builder); + } + WCMD_appendCommand(cmd_op, cmd, redir, &left); } while (!done); #undef ERROR_IF *result = left; @@ -2479,7 +2518,7 @@ static void lexer_push_command(struct node_builder *builder,
if (redirs) redirs[*redirLen] = 0; /* Create redirects, keeping order (eg "2>foo 1>&2") */ - insrt = &thisEntry->redirects; + insrt = &tkn_pmt.redirection; *insrt = NULL; for (pos = redirs; pos; insrt = &(*insrt)->next) { @@ -2513,19 +2552,17 @@ static void lexer_push_command(struct node_builder *builder, } pos = p + 1; } + if (tkn_pmt.redirection) + node_builder_push_token_parameter(builder, TKN_REDIRECTION, tkn_pmt);
/* Reset the lengths */ *commandLen = 0; *redirLen = 0; *copyToLen = commandLen; *copyTo = command; - } else - { thisEntry->command = NULL; - thisEntry->redirects = NULL; - }
/* Fill in other fields */ thisEntry->pipeFile[0] = 0x00; @@ -3603,7 +3640,7 @@ CMD_NODE *WCMD_process_commands(CMD_NODE *thisCmd, BOOL oneBracket, Also, skip over any batch labels (eg. :fred) */ 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); + WCMD_execute(CMD_node_get_command(thisCmd)->command, CMD_node_get_single_node(thisCmd)->redirects, &thisCmd, retrycall); }
/* Step on unless the command itself already stepped on */
From: Eric Pouech epouech@codeweavers.com
(in yet to come patch).
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/wcmdmain.c | 98 ++++++++++++++++++++--------------------- 1 file changed, 49 insertions(+), 49 deletions(-)
diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index 13ed0e09bab..e2c60f3b8c0 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -1098,55 +1098,6 @@ void for_control_append_set(CMD_FOR_CONTROL *for_ctrl, const WCHAR *set) for_ctrl->set = xstrdupW(set); }
-/*************************************************************************** - * node_dispose_tree - * - * Frees the storage held for a parsed command line - * - This is not done in the process_commands, as eventually the current - * pointer will be modified within the commands, and hence a single free - * routine is simpler - */ -void node_dispose_tree(CMD_NODE *cmds) -{ - /* Loop through the commands, freeing them one by one */ - while (cmds) - { - CMD_NODE *thisCmd = cmds; - cmds = CMD_node_next(cmds); - if (thisCmd->op == CMD_SINGLE) - { - redirection_dispose_list(thisCmd->redirects); - command_dispose(thisCmd->command); - } - else - node_dispose_tree(thisCmd->left); - free(thisCmd); - } -} - -static CMD_NODE *node_create_single(CMD_COMMAND *c, CMD_REDIRECTION *redir) -{ - CMD_NODE *new = xalloc(sizeof(CMD_NODE)); - - new->op = CMD_SINGLE; - new->command = c; - new->redirects = redir; - - 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; - new->redirects = NULL; - - return new; -} - void if_condition_dispose(CMD_IF_CONDITION *cond) { switch (cond->op) @@ -1287,6 +1238,55 @@ const char *debugstr_if_condition(const CMD_IF_CONDITION *cond) } }
+/*************************************************************************** + * node_dispose_tree + * + * Frees the storage held for a parsed command line + * - This is not done in the process_commands, as eventually the current + * pointer will be modified within the commands, and hence a single free + * routine is simpler + */ +void node_dispose_tree(CMD_NODE *cmds) +{ + /* Loop through the commands, freeing them one by one */ + while (cmds) + { + CMD_NODE *thisCmd = cmds; + cmds = CMD_node_next(cmds); + if (thisCmd->op == CMD_SINGLE) + { + redirection_dispose_list(thisCmd->redirects); + command_dispose(thisCmd->command); + } + else + node_dispose_tree(thisCmd->left); + free(thisCmd); + } +} + +static CMD_NODE *node_create_single(CMD_COMMAND *c, CMD_REDIRECTION *redir) +{ + CMD_NODE *new = xalloc(sizeof(CMD_NODE)); + + new->op = CMD_SINGLE; + new->command = c; + new->redirects = redir; + + 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; + new->redirects = NULL; + + return new; +} + static void init_msvcrt_io_block(STARTUPINFOW* st) { STARTUPINFOW st_p;
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/cmd.rc | 1 + programs/cmd/wcmd.h | 1 + programs/cmd/wcmdmain.c | 23 +++++++++++++++++++++-- 3 files changed, 23 insertions(+), 2 deletions(-)
diff --git a/programs/cmd/cmd.rc b/programs/cmd/cmd.rc index 59cb570b167..3bcf0ad4874 100644 --- a/programs/cmd/cmd.rc +++ b/programs/cmd/cmd.rc @@ -408,4 +408,5 @@ Enter HELP <command> for further information on any of the above commands.\n" WCMD_BADHEXOCT, "Badly formed number - must be one of decimal (12),\n hexadecimal (0x34) or octal (056).\n" WCMD_FILENAMETOOLONG, "File name is too long.\n" WCMD_BADTOKEN, "Syntax error: unexpected %1\n" + WCMD_ENDOFLINE, "End of line" } diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index 6230646bce6..f62ee3da6a9 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -482,3 +482,4 @@ extern WCHAR version_string[]; #define WCMD_BADHEXOCT 1045 #define WCMD_FILENAMETOOLONG 1046 #define WCMD_BADTOKEN 1047 +#define WCMD_ENDOFLINE 1048 diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index e2c60f3b8c0..12d3db1b012 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -2466,15 +2466,34 @@ static BOOL node_builder_generate(struct node_builder *builder, CMD_NODE **node) /* print error on first unused token */ if (builder->pos < builder->num) { + WCHAR buffer[MAXSTRING]; + const WCHAR *tknstr; + tkn = node_builder_peek_next_token(builder, &tkn_pmt); switch (tkn) { case TKN_COMMAND: - WCMD_output_stderr(WCMD_LoadMessage(WCMD_BADTOKEN), tkn_pmt.command->command); + tknstr = tkn_pmt.command->command; + break; + case TKN_EOL: + tknstr = WCMD_LoadMessage(WCMD_ENDOFLINE); + break; + case TKN_REDIRECTION: + MultiByteToWideChar(CP_ACP, 0, debugstr_redirection(tkn_pmt.redirection), -1, buffer, ARRAY_SIZE(buffer)); + tknstr = buffer; + break; + case TKN_AMP: + case TKN_AMPAMP: + case TKN_BAR: + case TKN_BARBAR: + MultiByteToWideChar(CP_ACP, 0, debugstr_token(tkn, tkn_pmt), -1, buffer, ARRAY_SIZE(buffer)); + tknstr = buffer; break; default: - WCMD_output_stderr(WCMD_LoadMessage(WCMD_BADTOKEN), debugstr_token(tkn, tkn_pmt)); + FIXME("Unexpected situation\n"); + tknstr = L""; } + WCMD_output_stderr(WCMD_LoadMessage(WCMD_BADTOKEN), tknstr); } /* free remaining tokens */ for (;;)