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 */