From: Francis De Brabandere <francisdb@gmail.com> --- dlls/vbscript/global.c | 181 +++++++++++++++++++++++++++++++++++- dlls/vbscript/tests/api.vbs | 66 +++++++++++++ 2 files changed, 244 insertions(+), 3 deletions(-) diff --git a/dlls/vbscript/global.c b/dlls/vbscript/global.c index 1d244b8207f..bc4d7d69347 100644 --- a/dlls/vbscript/global.c +++ b/dlls/vbscript/global.c @@ -2575,10 +2575,185 @@ static HRESULT Global_Erase(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, return E_NOTIMPL; } -static HRESULT Global_Filter(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +static HRESULT Global_Filter(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res) { - FIXME("\n"); - return E_NOTIMPL; + VARIANT *data; + SAFEARRAY *sa, *out_sa; + SAFEARRAYBOUND bounds; + LONG lbound, ubound, i, match_count; + BSTR search, conv_search = NULL, str, conv_str; + int include = 1, mode = 0, found; + HRESULT hres; + + TRACE("%s %u...\n", debugstr_variant(args), args_cnt); + + assert(2 <= args_cnt && args_cnt <= 4); + + if(V_VT(args) == VT_NULL || V_VT(args+1) == VT_NULL + || (args_cnt > 2 && V_VT(args+2) == VT_NULL) + || (args_cnt > 3 && V_VT(args+3) == VT_NULL)) + return MAKE_VBSERROR(VBSE_ILLEGAL_NULL_USE); + + switch(V_VT(args)) { + case VT_VARIANT|VT_ARRAY: + sa = V_ARRAY(args); + break; + case VT_VARIANT|VT_ARRAY|VT_BYREF: + sa = *V_ARRAYREF(args); + break; + default: + return MAKE_VBSERROR(VBSE_TYPE_MISMATCH); + } + + if(V_VT(args+1) == VT_BSTR) { + search = V_BSTR(args+1); + }else { + hres = to_string(args+1, &conv_search); + if(FAILED(hres)) + return hres; + search = conv_search; + } + + if(args_cnt > 2) { + hres = to_int(args+2, &include); + if(FAILED(hres)) + goto done; + } + + if(args_cnt > 3) { + hres = to_int(args+3, &mode); + if(FAILED(hres)) + goto done; + if(mode != 0 && mode != 1) { + hres = MAKE_VBSERROR(VBSE_ILLEGAL_FUNC_CALL); + goto done; + } + } + + if(SafeArrayGetDim(sa) != 1) { + hres = MAKE_VBSERROR(VBSE_TYPE_MISMATCH); + goto done; + } + + hres = SafeArrayGetLBound(sa, 1, &lbound); + if(FAILED(hres)) + goto done; + hres = SafeArrayGetUBound(sa, 1, &ubound); + if(FAILED(hres)) + goto done; + + hres = SafeArrayAccessData(sa, (void**)&data); + if(FAILED(hres)) + goto done; + + /* First pass: count matches */ + match_count = 0; + for(i = lbound; i <= ubound; i++) { + conv_str = NULL; + if(V_VT(&data[i - lbound]) == VT_BSTR) { + str = V_BSTR(&data[i - lbound]); + }else { + hres = to_string(&data[i - lbound], &conv_str); + if(FAILED(hres)) { + SafeArrayUnaccessData(sa); + goto done; + } + str = conv_str; + } + + if(!SysStringLen(search)) + found = 1; + else + found = FindStringOrdinal(FIND_FROMSTART, str, SysStringLen(str), + search, SysStringLen(search), mode) >= 0; + + if(conv_str) + SysFreeString(conv_str); + + if(include ? found : !found) + match_count++; + } + + /* Create result array */ + bounds.lLbound = 0; + bounds.cElements = match_count ? match_count : 0; + out_sa = SafeArrayCreate(VT_VARIANT, 1, &bounds); + if(!out_sa) { + SafeArrayUnaccessData(sa); + hres = E_OUTOFMEMORY; + goto done; + } + + if(match_count) { + VARIANT *out_data; + LONG out_i = 0; + + hres = SafeArrayAccessData(out_sa, (void**)&out_data); + if(FAILED(hres)) { + SafeArrayUnaccessData(sa); + SafeArrayDestroy(out_sa); + goto done; + } + + /* Second pass: copy matches */ + for(i = lbound; i <= ubound; i++) { + conv_str = NULL; + if(V_VT(&data[i - lbound]) == VT_BSTR) { + str = V_BSTR(&data[i - lbound]); + }else { + hres = to_string(&data[i - lbound], &conv_str); + if(FAILED(hres)) { + SafeArrayUnaccessData(out_sa); + SafeArrayUnaccessData(sa); + SafeArrayDestroy(out_sa); + goto done; + } + str = conv_str; + } + + if(!SysStringLen(search)) + found = 1; + else + found = FindStringOrdinal(FIND_FROMSTART, str, SysStringLen(str), + search, SysStringLen(search), mode) >= 0; + + if(include ? found : !found) { + V_VT(&out_data[out_i]) = VT_BSTR; + V_BSTR(&out_data[out_i]) = SysAllocString(str); + if(!V_BSTR(&out_data[out_i])) { + if(conv_str) + SysFreeString(conv_str); + SafeArrayUnaccessData(out_sa); + SafeArrayUnaccessData(sa); + SafeArrayDestroy(out_sa); + hres = E_OUTOFMEMORY; + goto done; + } + out_i++; + } + + if(conv_str) + SysFreeString(conv_str); + } + + SafeArrayUnaccessData(out_sa); + } + + SafeArrayUnaccessData(sa); + + if(res) { + V_VT(res) = VT_ARRAY|VT_VARIANT; + V_ARRAY(res) = out_sa; + }else { + SafeArrayDestroy(out_sa); + } + + SysFreeString(conv_search); + return S_OK; + +done: + SysFreeString(conv_search); + return hres; } static HRESULT Global_Join(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res) diff --git a/dlls/vbscript/tests/api.vbs b/dlls/vbscript/tests/api.vbs index e18d887306b..ad534be0082 100644 --- a/dlls/vbscript/tests/api.vbs +++ b/dlls/vbscript/tests/api.vbs @@ -2624,4 +2624,70 @@ end sub call testFormatNumber() call testFormatNumberError() +' Filter tests +Dim filterArr, filterResult +filterArr = Array("apple", "banana", "grape", "pineapple", "orange") + +' Include matches (default) +filterResult = Filter(filterArr, "ap") +Call ok(UBound(filterResult) = 2, "Filter include UBound = " & UBound(filterResult)) +Call ok(LBound(filterResult) = 0, "Filter include LBound = " & LBound(filterResult)) +Call ok(filterResult(0) = "apple", "Filter include (0) = " & filterResult(0)) +Call ok(filterResult(1) = "grape", "Filter include (1) = " & filterResult(1)) +Call ok(filterResult(2) = "pineapple", "Filter include (2) = " & filterResult(2)) + +' Exclude matches +filterResult = Filter(filterArr, "ap", False) +Call ok(UBound(filterResult) = 1, "Filter exclude UBound = " & UBound(filterResult)) +Call ok(filterResult(0) = "banana", "Filter exclude (0) = " & filterResult(0)) +Call ok(filterResult(1) = "orange", "Filter exclude (1) = " & filterResult(1)) + +' Case-sensitive (default binary compare) +filterResult = Filter(filterArr, "AP") +Call ok(UBound(filterResult) = -1, "Filter case-sensitive UBound = " & UBound(filterResult)) + +' Case-insensitive (text compare) +filterResult = Filter(filterArr, "AP", True, 1) +Call ok(UBound(filterResult) = 2, "Filter text compare UBound = " & UBound(filterResult)) + +' Empty search string returns all elements +filterResult = Filter(filterArr, "") +Call ok(UBound(filterResult) = UBound(filterArr), "Filter empty search UBound = " & UBound(filterResult)) + +' No matches returns empty array +filterResult = Filter(filterArr, "xyz") +Call ok(UBound(filterResult) = -1, "Filter no match UBound = " & UBound(filterResult)) + +' Single element match +filterResult = Filter(Array("hello"), "ell") +Call ok(UBound(filterResult) = 0, "Filter single match UBound = " & UBound(filterResult)) +Call ok(filterResult(0) = "hello", "Filter single match (0) = " & filterResult(0)) + +' Single element no match +filterResult = Filter(Array("hello"), "xyz") +Call ok(UBound(filterResult) = -1, "Filter single no match UBound = " & UBound(filterResult)) + +sub testFilterError() + on error resume next + dim r + + call Err.clear() + r = Filter(Null, "test") + Call ok(Err.number = 94, "Filter(Null) Err.number = " & Err.number) + + call Err.clear() + r = Filter(filterArr, Null) + Call ok(Err.number = 94, "Filter(arr, Null) Err.number = " & Err.number) + + call Err.clear() + r = Filter("not_array", "test") + Call ok(Err.number = 13, "Filter(string) Err.number = " & Err.number) + + call Err.clear() + r = Filter(filterArr, "ap", True, 5) + Call ok(Err.number = 5, "Filter(mode=5) Err.number = " & Err.number) +end sub + +call testFilterError() + Call reportSuccess() -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10505