This is part XXVI of cmd engine rewrite.
It mainly tackles which variable name (single letter) are supported as FOR loop variables. We recently extended the scope of letters (a-z, A-Z) with digits (0-9). A couple of bug reports show that more are supported. More manual testing show that all 7bit ASCII characters are supported (except the ones handled by lexer like CR, LR, \0, <SPACE>, <TAB>, ctrl-Z...).
This serie: - add a couple of new tests for FOR loop variables (also testing limits on tokens= options for setting at once several contiguous variables) - fix all exhibited bugs - rewrite parsing and handling of tokens= option (simplicity, correctness).
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/tests/test_builtins.cmd | 11 +++++++++-- programs/cmd/tests/test_builtins.cmd.exp | 7 ++++++- 2 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd index 01c8a73ff31..8107735dbb3 100644 --- a/programs/cmd/tests/test_builtins.cmd +++ b/programs/cmd/tests/test_builtins.cmd @@ -2434,14 +2434,21 @@ for /f "tokens=1,2,3*" %%i in ("a b c d e f g") do echo h=%%h i=%%i j=%%j k=%%k for /f "tokens=3,2,1*" %%i in ("a b c d e f g") do echo h=%%h i=%%i j=%%j k=%%k l=%%l m=%%m n=%%n o=%%o rem Duplicates are ignored for /f "tokens=1,2,1*" %%i in ("a b c d e f g") do echo h=%%h i=%%i j=%%j k=%%k l=%%l m=%%m n=%%n o=%%o +rem errors can exist +(for /f "tokens=1,2*,4" %%i in ("a b c d e f g") do echo h=%%h i=%%i j=%%j k=%%k l=%%l m=%%m n=%%n o=%%o) || echo failure %%i rem Large tokens are allowed for /f "tokens=25,1,5*" %%i in ("a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z") do echo h=%%h i=%%i j=%%j k=%%k l=%%l m=%%m n=%%n o=%%o rem Show tokens blanked in advance regardless of uniqueness of requested tokens for /f "tokens=1,1,1,2*" %%i in ("a b c d e f g") do echo h=%%h i=%%i j=%%j k=%%k l=%%l m=%%m n=%%n o=%%o for /f "tokens=1-2,1-2,1-2" %%i in ("a b c d e f g") do echo h=%%h i=%%i j=%%j k=%%k l=%%l m=%%m n=%%n o=%%o -rem Show No wrapping from z to A BUT wrapping sort of occurs Z to a occurs +rem Show mapping of most of the ASCII characters (on top of letters & digits) for /f "tokens=1-20" %%u in ("a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z") do echo u=%%u v=%%v w=%%w x=%%x y=%%y z=%%z A=%%A a=%%a -for /f "tokens=1-20" %%U in ("a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z") do echo U=%%U V=%%V W=%%W X=%%X Y=%%Y Z=%%Z A=%%A a=%%a +for /f "tokens=1-20" %%U in ("a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z") do echo U=%%U V=%%V W=%%W X=%%X Y=%%Y Z=%%Z ^[=%%^[ ^=%%^\ ^]=%%^] ^^=%%^^ _=%%_ `=%%` A=%%A a=%%a +rem Testing limits (max number of contiguous variables, limit at 127) +(for /f "tokens=1-31" %%A in ("a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z") do echo U=%%U V=%%V W=%%W X=%%X Y=%%Y Z=%%Z ^[=%%^[ ^=%%^\ ^]=%%^] ^^=%%^^ _=%%_ `=%%` A=%%A a=%%a) || echo failure +(for /f "tokens=1-32" %%A in ("a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z") do echo U=%%U V=%%V W=%%W X=%%X Y=%%Y Z=%%Z ^[=%%^[ ^=%%^\ ^]=%%^] ^^=%%^^ _=%%_ `=%%` A=%%A a=%%a) || echo failure %%A +for /f "tokens=1-20" %%} in ("a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z") do echo ^}=%%^} ^~=%%^~ +echo ---- temp rem Show negative ranges have no effect for /f "tokens=1-3,5" %%i in ("a b c d e f g") do echo h=%%h i=%%i j=%%j k=%%k l=%%l m=%%m o=%%o for /f "tokens=3-1,5" %%i in ("a b c d e f g") do echo h=%%h i=%%i j=%%j k=%%k l=%%l m=%%m o=%%o diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index a58a0254e16..0898e6997ec 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -1595,11 +1595,16 @@ h=%h i=f j=i k=j k l m n;;== o p q r s t u v w x y z l=%l m=%m o=%o h=%h i=a j=b k=c l=d e f g m=%m n=%n o=%o h=%h i=a j=b k=c l=d e f g m=%m n=%n o=%o h=%h i=a j=b k= l= m=%m n=%n o=%o +@todo_wine@failure %i h=%h i=a j=e k=y l=z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z m=%m n=%n o=%o h=%h i=a j=b k= l= m= n=%n o=%o h=%h i=a j=b k= l= m= n= o=%o u=a v=b w=c x=d y=e z=f A=%A a=%a -@todo_wine@U=a V=b W=c X=d Y=e Z=f A=%A a=m +@todo_wine@U=a V=b W=c X=d Y=e Z=f [=g =h ]=i ^=j _=k `=l A=%A a=m +@todo_wine@U=u V=v W=w X=x Y=y Z=z [=A =B ]=C ^=D _=E `=%` A=a a=%a +@todo_wine@failure %A +@todo_wine@}=a ~=b +---- temp h=%h i=a j=b k=c l=e m=%m o=%o@or_broken@h=%h i=a j=b k=c l=e m= o=%o h=%h i=e j=%j k=%k l=%l m=%m o=%o h=%h i=a j=b k=c l=d e f g m=%m n=%n o=%o@or_broken@h=%h i=a j=b k=c l=d e f g m= n=%n o=%o
From: Eric Pouech epouech@codeweavers.com
Wine-bug: https://bugs.winehq.org/show_bug.cgi?id=57148
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/batch.c | 8 ++-- programs/cmd/tests/test_builtins.cmd.exp | 4 +- programs/cmd/wcmd.h | 40 ++++++----------- programs/cmd/wcmdmain.c | 57 ++++++++++++------------ 4 files changed, 46 insertions(+), 63 deletions(-)
diff --git a/programs/cmd/batch.c b/programs/cmd/batch.c index 717639ed9b2..25281df15f0 100644 --- a/programs/cmd/batch.c +++ b/programs/cmd/batch.c @@ -369,9 +369,8 @@ void WCMD_HandleTildeModifiers(WCHAR **start, BOOL atExecute) break;
} else { - int foridx = for_var_char_to_index(*lastModifier); /* Its a valid parameter identifier - OK */ - if ((foridx >= 0) && (forloopcontext->variable[foridx] != NULL)) break; + if (for_var_is_valid(*lastModifier) && forloopcontext->variable[*lastModifier] != NULL) break;
/* Its not a valid parameter identifier - step backwards */ lastModifier--; @@ -397,9 +396,8 @@ void WCMD_HandleTildeModifiers(WCHAR **start, BOOL atExecute) *lastModifier-'0' + context -> shift_count[*lastModifier-'0'], NULL, FALSE, TRUE)); } else { - int foridx = for_var_char_to_index(*lastModifier); - if (foridx != -1) - lstrcpyW(outputparam, forloopcontext->variable[foridx]); + if (for_var_is_valid(*lastModifier)) + lstrcpyW(outputparam, forloopcontext->variable[*lastModifier]); }
/* 1. Handle '~' : Strip surrounding quotes */ diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 0898e6997ec..2796acc94f5 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -1600,8 +1600,8 @@ h=%h i=a j=e k=y l=z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z m=%m n= h=%h i=a j=b k= l= m= n=%n o=%o h=%h i=a j=b k= l= m= n= o=%o u=a v=b w=c x=d y=e z=f A=%A a=%a -@todo_wine@U=a V=b W=c X=d Y=e Z=f [=g =h ]=i ^=j _=k `=l A=%A a=m -@todo_wine@U=u V=v W=w X=x Y=y Z=z [=A =B ]=C ^=D _=E `=%` A=a a=%a +U=a V=b W=c X=d Y=e Z=f [=g =h ]=i ^=j _=k `=l A=%A a=m +U=u V=v W=w X=x Y=y Z=z [=A =B ]=C ^=D _=E `=%` A=a a=%a @todo_wine@failure %A @todo_wine@}=a ~=b ---- temp diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index ecb729efa00..85e628db690 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -92,7 +92,7 @@ typedef struct _CMD_FOR_CONTROL enum for_control_operator {CMD_FOR_FILETREE, CMD_FOR_FILE_SET /* /F */, CMD_FOR_NUMBERS /* /L */} operator; unsigned flags; /* |-ed CMD_FOR_FLAG_* */ - int variable_index; + unsigned variable_index; const WCHAR *set; union { @@ -293,41 +293,28 @@ typedef struct _DIRECTORY_STACK WCHAR *fileName; } DIRECTORY_STACK;
-/* Data structure to for loop variables during for body execution, bearing - in mind that for loops can be nested */ -#define MAX_FOR_VARIABLES (2*26+10) - -static inline int for_var_char_to_index(WCHAR c) -{ - if (c >= L'a' && c <= L'z') return c - L'a'; - if (c >= L'A' && c <= L'Z') return c - L'A' + 26; - if (c >= L'0' && c <= L'9') return c - L'0' + 2 * 26; - return -1; -} - -static inline WCHAR for_var_index_to_char(int var_idx) -{ - if (var_idx < 0 || var_idx >= MAX_FOR_VARIABLES) return L'?'; - if (var_idx < 26) return L'a' + var_idx; - if (var_idx < 52) return L'A' + var_idx - 26; - return L'0' + var_idx - 52; -} - -/* check that the range [var_idx, var_idx + var_offset] is a contiguous range */ -static inline BOOL for_var_index_in_range(int var_idx, int var_offset) +static inline const char *debugstr_for_var(WCHAR ch) { - return for_var_char_to_index(for_var_index_to_char(var_idx) + var_offset) == var_idx + var_offset; + static char tmp[16]; + if (iswprint(ch)) + sprintf(tmp, "%%%lc", ch); + else + sprintf(tmp, "%%[%x]", ch); + return tmp; }
typedef struct _FOR_CONTEXT { struct _FOR_CONTEXT *previous; - WCHAR *variable[MAX_FOR_VARIABLES]; /* a-z then A-Z */ + WCHAR *variable[128]; } FOR_CONTEXT;
+extern FOR_CONTEXT *forloopcontext; +static inline BOOL for_var_is_valid(WCHAR ch) {return ch && ch < ARRAY_SIZE(forloopcontext->variable);} + void WCMD_save_for_loop_context(BOOL reset); void WCMD_restore_for_loop_context(void); -void WCMD_set_for_loop_variable(int var_idx, const WCHAR *value); +void WCMD_set_for_loop_variable(unsigned varidx, const WCHAR *value);
/* * Global variables quals, param1, param2 contain the current qualifiers @@ -337,7 +324,6 @@ void WCMD_set_for_loop_variable(int var_idx, const WCHAR *value); extern WCHAR quals[MAXSTRING], param1[MAXSTRING], param2[MAXSTRING]; extern int errorlevel; extern BATCH_CONTEXT *context; -extern FOR_CONTEXT *forloopcontext; extern BOOL delayedsubst;
static inline BOOL WCMD_is_in_context(const WCHAR *ext) diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index be369686c00..eb38d0ffd17 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -720,11 +720,10 @@ static void handleExpansion(WCHAR *cmd, BOOL atExecute) { WCHAR *normalp;
/* Display the FOR variables in effect */ - for (i=0;i<MAX_FOR_VARIABLES;i++) { + for (i=0;i<ARRAY_SIZE(forloopcontext->variable);i++) { if (forloopcontext->variable[i]) { - WINE_TRACE("FOR variable context: %c = '%s'\n", - for_var_index_to_char(i), - wine_dbgstr_w(forloopcontext->variable[i])); + TRACE("FOR variable context: %s = '%s'\n", + debugstr_for_var((WCHAR)i), wine_dbgstr_w(forloopcontext->variable[i])); } }
@@ -770,10 +769,9 @@ static void handleExpansion(WCHAR *cmd, BOOL atExecute) { p = WCMD_strsubstW(p, p+2, NULL, 0);
} else { - int forvaridx = for_var_char_to_index(*(p+1)); - if (startchar == L'%' && forvaridx != -1 && forloopcontext->variable[forvaridx]) { + if (startchar == L'%' && for_var_is_valid(p[1]) && forloopcontext->variable[p[1]]) { /* Replace the 2 characters, % and for variable character */ - p = WCMD_strsubstW(p, p + 2, forloopcontext->variable[forvaridx], -1); + p = WCMD_strsubstW(p, p + 2, forloopcontext->variable[p[1]], -1); } else if (!atExecute || startchar == L'!') { BOOL first = p == cmd; p = WCMD_expand_envvar(p); @@ -974,16 +972,16 @@ const char *debugstr_for_control(const CMD_FOR_CONTROL *for_ctrl) options = ""; break; } - return wine_dbg_sprintf("[FOR] %s %s%s%%%c (%ls)", + return wine_dbg_sprintf("[FOR] %s %s%s%s (%ls)", for_ctrl_strings[for_ctrl->operator], flags, options, - for_var_index_to_char(for_ctrl->variable_index), for_ctrl->set); + debugstr_for_var(for_ctrl->variable_index), for_ctrl->set); }
-static void for_control_create(enum for_control_operator for_op, unsigned flags, const WCHAR *options, int var_idx, CMD_FOR_CONTROL *for_ctrl) +static void for_control_create(enum for_control_operator for_op, unsigned flags, const WCHAR *options, unsigned varidx, CMD_FOR_CONTROL *for_ctrl) { for_ctrl->operator = for_op; for_ctrl->flags = flags; - for_ctrl->variable_index = var_idx; + for_ctrl->variable_index = varidx; for_ctrl->set = NULL; switch (for_ctrl->operator) { @@ -995,13 +993,13 @@ static void for_control_create(enum for_control_operator for_op, unsigned flags, } }
-static void for_control_create_fileset(unsigned flags, int var_idx, WCHAR eol, int num_lines_to_skip, BOOL use_backq, +static void for_control_create_fileset(unsigned flags, unsigned varidx, WCHAR eol, int num_lines_to_skip, BOOL use_backq, const WCHAR *delims, const WCHAR *tokens, CMD_FOR_CONTROL *for_ctrl) { for_ctrl->operator = CMD_FOR_FILE_SET; for_ctrl->flags = flags; - for_ctrl->variable_index = var_idx; + for_ctrl->variable_index = varidx; for_ctrl->set = NULL;
for_ctrl->eol = eol; @@ -2024,7 +2022,7 @@ static CMD_FOR_CONTROL *for_control_parse(WCHAR *opts_var) WCHAR *arg; unsigned flags = 0; int arg_index; - int var_idx; + unsigned varidx;
options[0] = L'\0'; /* native allows two options only in the /D /R case, a repetition of the option @@ -2106,8 +2104,9 @@ static CMD_FOR_CONTROL *for_control_parse(WCHAR *opts_var)
/* Ensure line continues with variable */ arg = WCMD_parameter(opts_var, arg_index++, NULL, FALSE, FALSE); - if (!arg || *arg != L'%' || (var_idx = for_var_char_to_index(arg[1])) == -1) + if (!arg || *arg != L'%' || !for_var_is_valid(arg[1])) goto syntax_error; /* FIXME native prints the offending token "%<whatever>" was unexpected at this time */ + varidx = arg[1]; for_ctrl = xalloc(sizeof(*for_ctrl)); if (for_op == CMD_FOR_FILE_SET) { @@ -2171,12 +2170,12 @@ static CMD_FOR_CONTROL *for_control_parse(WCHAR *opts_var) goto syntax_error; } } - for_control_create_fileset(flags, var_idx, eol, num_lines_to_skip, use_backq, + for_control_create_fileset(flags, varidx, eol, num_lines_to_skip, use_backq, delims ? delims : xstrdupW(L" \t"), tokens ? tokens : xstrdupW(L"1"), for_ctrl); } else - for_control_create(for_op, flags, options, var_idx, for_ctrl); + for_control_create(for_op, flags, options, varidx, for_ctrl); return for_ctrl; syntax_error: WCMD_output_stderr(WCMD_LoadMessage(WCMD_SYNTAXERR)); @@ -3261,7 +3260,7 @@ static BOOL if_condition_evaluate(CMD_IF_CONDITION *cond, int *test) return TRUE; }
-static RETURN_CODE for_loop_fileset_parse_line(CMD_NODE *node, int varidx, WCHAR *buffer, +static RETURN_CODE for_loop_fileset_parse_line(CMD_NODE *node, unsigned varidx, WCHAR *buffer, WCHAR forf_eol, const WCHAR *forf_delims, const WCHAR *forf_tokens) { RETURN_CODE return_code = NO_ERROR; @@ -3289,10 +3288,10 @@ static RETURN_CODE for_loop_fileset_parse_line(CMD_NODE *node, int varidx, WCHAR nexttoken = WCMD_for_nexttoken(lasttoken, forf_tokens, &totalfound, &starfound, &thisduplicate);
- TRACE("Using var=%lc on %d max\n", for_var_index_to_char(varidx), totalfound); + TRACE("Using var=%s on %d max\n", debugstr_for_var(varidx), totalfound); /* Empty out variables */ for (varoffset = 0; - varoffset < totalfound && for_var_index_in_range(varidx, varoffset); + varoffset < totalfound && for_var_is_valid(varidx + varoffset); varoffset++) WCMD_set_for_loop_variable(varidx + varoffset, emptyW);
@@ -3306,7 +3305,7 @@ static RETURN_CODE for_loop_fileset_parse_line(CMD_NODE *node, int varidx, WCHAR { anyduplicates |= thisduplicate;
- if (!for_var_index_in_range(varidx, varoffset)) + if (!for_var_is_valid(varidx + varoffset)) { WARN("Out of range offset\n"); break; @@ -3329,7 +3328,7 @@ static RETURN_CODE for_loop_fileset_parse_line(CMD_NODE *node, int varidx, WCHAR /* If all the rest of the tokens were requested, and there is still space in * the variable range, write them now */ - if (!anyduplicates && starfound && for_var_index_in_range(varidx, varoffset)) + if (!anyduplicates && starfound && for_var_is_valid(varidx + varoffset)) { nexttoken++; WCMD_parameter_with_delims(buffer, (nexttoken-1), &parm, FALSE, FALSE, forf_delims); @@ -3365,13 +3364,13 @@ void WCMD_save_for_loop_context(BOOL reset) void WCMD_restore_for_loop_context(void) { FOR_CONTEXT *old = forloopcontext->previous; - int varidx; + unsigned varidx; if (!old) { FIXME("Unexpected situation\n"); return; } - for (varidx = 0; varidx < MAX_FOR_VARIABLES; varidx++) + for (varidx = 0; varidx < ARRAY_SIZE(forloopcontext->variable); varidx++) { if (forloopcontext->variable[varidx] != old->variable[varidx]) free(forloopcontext->variable[varidx]); @@ -3380,13 +3379,13 @@ void WCMD_restore_for_loop_context(void) forloopcontext = old; }
-void WCMD_set_for_loop_variable(int var_idx, const WCHAR *value) +void WCMD_set_for_loop_variable(unsigned varidx, const WCHAR *value) { - if (var_idx < 0 || var_idx >= MAX_FOR_VARIABLES) return; + if (!for_var_is_valid(varidx)) return; if (forloopcontext->previous && - forloopcontext->previous->variable[var_idx] != forloopcontext->variable[var_idx]) - free(forloopcontext->variable[var_idx]); - forloopcontext->variable[var_idx] = xstrdupW(value); + forloopcontext->previous->variable[varidx] != forloopcontext->variable[varidx]) + free(forloopcontext->variable[varidx]); + forloopcontext->variable[varidx] = xstrdupW(value); }
static BOOL match_ending_delim(WCHAR *string)
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/tests/test_builtins.cmd | 1 - programs/cmd/tests/test_builtins.cmd.exp | 3 +-- programs/cmd/wcmdmain.c | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/programs/cmd/tests/test_builtins.cmd b/programs/cmd/tests/test_builtins.cmd index 8107735dbb3..9a673a40467 100644 --- a/programs/cmd/tests/test_builtins.cmd +++ b/programs/cmd/tests/test_builtins.cmd @@ -2448,7 +2448,6 @@ rem Testing limits (max number of contiguous variables, limit at 127) (for /f "tokens=1-31" %%A in ("a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z") do echo U=%%U V=%%V W=%%W X=%%X Y=%%Y Z=%%Z ^[=%%^[ ^=%%^\ ^]=%%^] ^^=%%^^ _=%%_ `=%%` A=%%A a=%%a) || echo failure (for /f "tokens=1-32" %%A in ("a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z") do echo U=%%U V=%%V W=%%W X=%%X Y=%%Y Z=%%Z ^[=%%^[ ^=%%^\ ^]=%%^] ^^=%%^^ _=%%_ `=%%` A=%%A a=%%a) || echo failure %%A for /f "tokens=1-20" %%} in ("a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z") do echo ^}=%%^} ^~=%%^~ -echo ---- temp rem Show negative ranges have no effect for /f "tokens=1-3,5" %%i in ("a b c d e f g") do echo h=%%h i=%%i j=%%j k=%%k l=%%l m=%%m o=%%o for /f "tokens=3-1,5" %%i in ("a b c d e f g") do echo h=%%h i=%%i j=%%j k=%%k l=%%l m=%%m o=%%o diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 2796acc94f5..4875e1db507 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -1603,8 +1603,7 @@ u=a v=b w=c x=d y=e z=f A=%A a=%a U=a V=b W=c X=d Y=e Z=f [=g =h ]=i ^=j _=k `=l A=%A a=m U=u V=v W=w X=x Y=y Z=z [=A =B ]=C ^=D _=E `=%` A=a a=%a @todo_wine@failure %A -@todo_wine@}=a ~=b ----- temp +}=a ~=b h=%h i=a j=b k=c l=e m=%m o=%o@or_broken@h=%h i=a j=b k=c l=e m= o=%o h=%h i=e j=%j k=%k l=%l m=%m o=%o h=%h i=a j=b k=c l=d e f g m=%m n=%n o=%o@or_broken@h=%h i=a j=b k=c l=d e f g m= n=%n o=%o diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index eb38d0ffd17..f87796f4d8a 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -747,7 +747,7 @@ static void handleExpansion(WCHAR *cmd, BOOL atExecute) { if (context) WCMD_strsubstW(p, p + 1, NULL, 0); if (!context || startchar == L'%') p++; /* Replace %~ modifications if in batch program */ - } else if (*(p+1) == '~') { + } else if (p[1] == L'~' && p[2] && !iswspace(p[2])) { WCMD_HandleTildeModifiers(&p, atExecute); p++;
From: Eric Pouech epouech@codeweavers.com
Signed-off-by: Eric Pouech epouech@codeweavers.com --- programs/cmd/builtins.c | 123 ---------------- programs/cmd/tests/test_builtins.cmd.exp | 4 +- programs/cmd/wcmd.h | 3 - programs/cmd/wcmdmain.c | 177 +++++++++++++++-------- 4 files changed, 115 insertions(+), 192 deletions(-)
diff --git a/programs/cmd/builtins.c b/programs/cmd/builtins.c index 7a7562d60d5..f656e496910 100644 --- a/programs/cmd/builtins.c +++ b/programs/cmd/builtins.c @@ -1640,129 +1640,6 @@ void WCMD_add_dirstowalk(DIRECTORY_STACK *dirsToWalk) FindClose(hff); }
-/************************************************************************** - * WCMD_for_nexttoken - * - * Parse the token= line, identifying the next highest number not processed - * so far. Count how many tokens are referred (including duplicates) and - * optionally return that, plus optionally indicate if the tokens= line - * ends in a star. - * - * Parameters: - * lasttoken [I] - Identifies the token index of the last one - * returned so far (-1 used for first loop) - * tokenstr [I] - The specified tokens= line - * firstCmd [O] - Optionally indicate how many tokens are listed - * doAll [O] - Optionally indicate if line ends with * - * duplicates [O] - Optionally indicate if there is any evidence of - * overlaying tokens in the string - * Note the caller should keep a running track of duplicates as the tokens - * are recursively passed. If any have duplicates, then the * token should - * not be honoured. - */ -int WCMD_for_nexttoken(int lasttoken, const WCHAR *tokenstr, - int *totalfound, BOOL *doall, - BOOL *duplicates) -{ - const WCHAR *pos = tokenstr; - int nexttoken = -1; - - if (totalfound) *totalfound = 0; - if (doall) *doall = FALSE; - if (duplicates) *duplicates = FALSE; - - WINE_TRACE("Find next token after %d in %s\n", lasttoken, - wine_dbgstr_w(tokenstr)); - - /* Loop through the token string, parsing it. Valid syntax is: - token=m or x-y with comma delimiter and optionally * to finish*/ - while (*pos) { - int nextnumber1, nextnumber2 = -1; - WCHAR *nextchar; - - /* Remember if the next character is a star, it indicates a need to - show all remaining tokens and should be the last character */ - if (*pos == '*') { - if (doall) *doall = TRUE; - if (totalfound) (*totalfound)++; - /* If we have not found a next token to return, then indicate - time to process the star */ - if (nexttoken == -1) { - if (lasttoken == -1) { - /* Special case the syntax of tokens=* which just means get whole line */ - nexttoken = 0; - } else { - nexttoken = lasttoken; - } - } - break; - } - - /* Get the next number */ - nextnumber1 = wcstoul(pos, &nextchar, 10); - - /* If it is followed by a minus, it's a range, so get the next one as well */ - if (*nextchar == '-') { - nextnumber2 = wcstoul(nextchar+1, &nextchar, 10); - - /* We want to return the lowest number that is higher than lasttoken - but only if range is positive */ - if (nextnumber2 >= nextnumber1 && - lasttoken < nextnumber2) { - - int nextvalue; - if (nexttoken == -1) { - nextvalue = max(nextnumber1, (lasttoken+1)); - } else { - nextvalue = min(nexttoken, max(nextnumber1, (lasttoken+1))); - } - - /* Flag if duplicates identified */ - if (nexttoken == nextvalue && duplicates) *duplicates = TRUE; - - nexttoken = nextvalue; - } - - /* Update the running total for the whole range */ - if (nextnumber2 >= nextnumber1 && totalfound) { - *totalfound = *totalfound + 1 + (nextnumber2 - nextnumber1); - } - pos = nextchar; - - } else if (pos != nextchar) { - if (totalfound) (*totalfound)++; - - /* See if the number found is one we have already seen */ - if (nextnumber1 == nexttoken && duplicates) *duplicates = TRUE; - - /* We want to return the lowest number that is higher than lasttoken */ - if (lasttoken < nextnumber1 && - ((nexttoken == -1) || (nextnumber1 < nexttoken))) { - nexttoken = nextnumber1; - } - pos = nextchar; - - } else { - /* Step on to the next character, usually over comma */ - if (*pos) pos++; - } - - } - - /* Return result */ - if (nexttoken == -1) { - WINE_TRACE("No next token found, previous was %d\n", lasttoken); - nexttoken = lasttoken; - } else if (nexttoken==lasttoken && doall && *doall) { - WINE_TRACE("Request for all remaining tokens now\n"); - } else { - WINE_TRACE("Found next token after %d was %d\n", lasttoken, nexttoken); - } - if (totalfound) WINE_TRACE("Found total tokens to be %d\n", *totalfound); - if (duplicates && *duplicates) WINE_TRACE("Duplicate numbers found\n"); - return nexttoken; -} - static int find_in_array(const WCHAR array[][10], size_t sz, const WCHAR *what) { int i; diff --git a/programs/cmd/tests/test_builtins.cmd.exp b/programs/cmd/tests/test_builtins.cmd.exp index 4875e1db507..67a308a4e8a 100644 --- a/programs/cmd/tests/test_builtins.cmd.exp +++ b/programs/cmd/tests/test_builtins.cmd.exp @@ -1595,14 +1595,14 @@ h=%h i=f j=i k=j k l m n;;== o p q r s t u v w x y z l=%l m=%m o=%o h=%h i=a j=b k=c l=d e f g m=%m n=%n o=%o h=%h i=a j=b k=c l=d e f g m=%m n=%n o=%o h=%h i=a j=b k= l= m=%m n=%n o=%o -@todo_wine@failure %i +failure %i h=%h i=a j=e k=y l=z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z m=%m n=%n o=%o h=%h i=a j=b k= l= m= n=%n o=%o h=%h i=a j=b k= l= m= n= o=%o u=a v=b w=c x=d y=e z=f A=%A a=%a U=a V=b W=c X=d Y=e Z=f [=g =h ]=i ^=j _=k `=l A=%A a=m U=u V=v W=w X=x Y=y Z=z [=A =B ]=C ^=D _=E `=%` A=a a=%a -@todo_wine@failure %A +failure %A }=a ~=b h=%h i=a j=b k=c l=e m=%m o=%o@or_broken@h=%h i=a j=b k=c l=e m= o=%o h=%h i=e j=%j k=%k l=%l m=%m o=%o diff --git a/programs/cmd/wcmd.h b/programs/cmd/wcmd.h index 85e628db690..caacd44995d 100644 --- a/programs/cmd/wcmd.h +++ b/programs/cmd/wcmd.h @@ -133,9 +133,6 @@ typedef struct _CMD_NODE }; }; } CMD_NODE; -int WCMD_for_nexttoken(int lasttoken, const WCHAR *tokenstr, - int *totalfound, BOOL *doall, - BOOL *duplicates);
struct _DIRECTORY_STACK; void WCMD_add_dirstowalk(struct _DIRECTORY_STACK *dirsToWalk); diff --git a/programs/cmd/wcmdmain.c b/programs/cmd/wcmdmain.c index f87796f4d8a..27c7def7900 100644 --- a/programs/cmd/wcmdmain.c +++ b/programs/cmd/wcmdmain.c @@ -3260,82 +3260,131 @@ static BOOL if_condition_evaluate(CMD_IF_CONDITION *cond, int *test) return TRUE; }
-static RETURN_CODE for_loop_fileset_parse_line(CMD_NODE *node, unsigned varidx, WCHAR *buffer, - WCHAR forf_eol, const WCHAR *forf_delims, const WCHAR *forf_tokens) +struct for_loop_variables { - RETURN_CODE return_code = NO_ERROR; - WCHAR *parm; - int varoffset; - int nexttoken, lasttoken = -1; - BOOL starfound = FALSE; - BOOL thisduplicate = FALSE; - BOOL anyduplicates = FALSE; - int totalfound; - static WCHAR emptyW[] = L""; - - /* Extract the parameters based on the tokens= value (There will always - be some value, as if it is not supplied, it defaults to tokens=1). - Rough logic: - Count how many tokens are named in the line, identify the lowest - Empty (set to null terminated string) that number of named variables - While lasttoken != nextlowest - %letter = parameter number 'nextlowest' - letter++ (if >26 or >52 abort) - Go through token= string finding next lowest number - If token ends in * set %letter = raw position of token(nextnumber+1) - */ - lasttoken = -1; - nexttoken = WCMD_for_nexttoken(lasttoken, forf_tokens, &totalfound, - &starfound, &thisduplicate); - - TRACE("Using var=%s on %d max\n", debugstr_for_var(varidx), totalfound); - /* Empty out variables */ - for (varoffset = 0; - varoffset < totalfound && for_var_is_valid(varidx + varoffset); - varoffset++) - WCMD_set_for_loop_variable(varidx + varoffset, emptyW); - - /* Loop extracting the tokens - * Note: nexttoken of 0 means there were no tokens requested, to handle - * the special case of tokens=* + unsigned char table[32]; + unsigned last, num_duplicates; + unsigned has_star; +}; + +static void for_loop_variables_init(struct for_loop_variables *flv) +{ + flv->last = flv->num_duplicates = 0; + flv->has_star = FALSE; +} + +static BOOL for_loop_variables_push(struct for_loop_variables *flv, unsigned char o) +{ + unsigned i; + for (i = 0; i < flv->last; i++) + if (flv->table[i] == o) + { + flv->num_duplicates++; + return TRUE; + } + if (flv->last >= ARRAY_SIZE(flv->table)) return FALSE; + flv->table[flv->last] = o; + flv->last++; + return TRUE; +} + +static int my_flv_compare(const void *a1, const void *a2) +{ + return *(const char*)a1 - *(const char*)a2; +} + +static unsigned for_loop_variables_max(const struct for_loop_variables *flv) +{ + return flv->last == 0 ? -1 : flv->table[flv->last - 1]; +} + +static BOOL for_loop_fill_variables(const WCHAR *forf_tokens, struct for_loop_variables *flv) +{ + const WCHAR *pos = forf_tokens; + + /* Loop through the token string, parsing it. + * Valid syntax is: token=m or x-y with comma delimiter and optionally * to finish */ - varoffset = 0; - TRACE("Parsing buffer into tokens: '%s'\n", wine_dbgstr_w(buffer)); - while (nexttoken > 0 && (nexttoken > lasttoken)) + while (*pos) { - anyduplicates |= thisduplicate; + unsigned num; + WCHAR *nextchar;
- if (!for_var_is_valid(varidx + varoffset)) + if (*pos == L'*') { - WARN("Out of range offset\n"); - break; + if (pos[1] != L'\0') return FALSE; + qsort(flv->table, flv->last, sizeof(flv->table[0]), my_flv_compare); + if (flv->num_duplicates) + { + flv->num_duplicates++; + return TRUE; + } + flv->has_star = TRUE; + return for_loop_variables_push(flv, for_loop_variables_max(flv) + 1); } - /* Extract the token number requested and set into the next variable context */ - parm = WCMD_parameter_with_delims(buffer, (nexttoken-1), NULL, TRUE, FALSE, forf_delims); - TRACE("Parsed token %d(%d) as parameter %s\n", nexttoken, - varidx + varoffset, wine_dbgstr_w(parm)); - if (parm) + + /* Get the next number */ + num = wcstoul(pos, &nextchar, 10); + if (!num || num >= 32) return FALSE; + num--; + /* range x-y */ + if (*nextchar == L'-') { - WCMD_set_for_loop_variable(varidx + varoffset, parm); - varoffset++; + unsigned int end = wcstoul(nextchar + 1, &nextchar, 10); + if (!end || end >= 32) return FALSE; + end--; + while (num <= end) + if (!for_loop_variables_push(flv, num++)) return FALSE; } + else if (!for_loop_variables_push(flv, num)) return FALSE;
- /* Find the next token */ - lasttoken = nexttoken; - nexttoken = WCMD_for_nexttoken(lasttoken, forf_tokens, NULL, - &starfound, &thisduplicate); + pos = nextchar; + if (*pos == L',') pos++; } - /* If all the rest of the tokens were requested, and there is still space in - * the variable range, write them now - */ - if (!anyduplicates && starfound && for_var_is_valid(varidx + varoffset)) + if (flv->last) + qsort(flv->table, flv->last, sizeof(flv->table[0]), my_flv_compare); + else + for_loop_variables_push(flv, 0); + return TRUE; +} + +static RETURN_CODE for_loop_fileset_parse_line(CMD_NODE *node, unsigned varidx, WCHAR *buffer, + WCHAR forf_eol, const WCHAR *forf_delims, const WCHAR *forf_tokens) +{ + RETURN_CODE return_code = NO_ERROR; + struct for_loop_variables flv; + WCHAR *parm; + unsigned i; + + for_loop_variables_init(&flv); + if (!for_loop_fill_variables(forf_tokens, &flv)) + { + TRACE("Error while parsing tokens=%ls\n", forf_tokens); + return ERROR_INVALID_FUNCTION; + } + + TRACE("Using var=%s on %u max%s\n", debugstr_for_var(varidx), flv.last, flv.has_star ? " with star" : ""); + /* Empty out variables */ + for (i = 0; i < flv.last + flv.num_duplicates; i++) + WCMD_set_for_loop_variable(varidx + i, L""); + + for (i = 0; i < flv.last; i++) { - nexttoken++; - WCMD_parameter_with_delims(buffer, (nexttoken-1), &parm, FALSE, FALSE, forf_delims); - TRACE("Parsed all remaining tokens (%d) as parameter %s\n", - varidx + varoffset, wine_dbgstr_w(parm)); + if (flv.has_star && i + 1 == flv.last) + { + WCMD_parameter_with_delims(buffer, flv.table[i], &parm, FALSE, FALSE, forf_delims); + TRACE("Parsed all remaining tokens %d(%s) as parameter %s\n", + flv.table[i], debugstr_for_var(varidx + i), wine_dbgstr_w(parm)); + if (parm) + WCMD_set_for_loop_variable(varidx + i, parm); + break; + } + /* Extract the token number requested and set into the next variable context */ + parm = WCMD_parameter_with_delims(buffer, flv.table[i], NULL, TRUE, FALSE, forf_delims); + TRACE("Parsed token %d(%s) as parameter %s\n", + flv.table[i], debugstr_for_var(varidx + i), wine_dbgstr_w(parm)); if (parm) - WCMD_set_for_loop_variable(varidx + varoffset, parm); + WCMD_set_for_loop_variable(varidx + i, parm); }
/* Execute the body of the for loop with these values */
Hi,
It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated.
The tests also ran into some preexisting test failures. If you know how to fix them that would be helpful. See the TestBot job for the details:
The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=148730
Your paranoid android.
=== debian11b (64 bit WoW report) ===
d3d9: d3d9ex.c:3236: Test failed: Got unexpected WINDOWPOS hwnd=0000000000000000, x=0, y=0, cx=0, cy=0, flags=0