[PATCH 0/1] MR10510: vbscript: Implement byte-string functions LenB, LeftB, RightB, MidB, InStrB, AscB, and ChrB.
These functions operate on raw bytes of the BSTR (UTF-16LE) data. Also fix InStrB registration to accept 2 args minimum, matching Windows. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10510
From: Francis De Brabandere <francisdb@gmail.com> These functions operate on raw bytes of the BSTR (UTF-16LE) data. Also fix InStrB registration to accept 2 args minimum, matching Windows. --- dlls/vbscript/global.c | 321 +++++++++++++++++++++++++++++++++--- dlls/vbscript/tests/api.vbs | 75 +++++++++ 2 files changed, 377 insertions(+), 19 deletions(-) diff --git a/dlls/vbscript/global.c b/dlls/vbscript/global.c index 1d244b8207f..d3faec3bdde 100644 --- a/dlls/vbscript/global.c +++ b/dlls/vbscript/global.c @@ -1298,8 +1298,28 @@ static HRESULT Global_Len(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VA static HRESULT Global_LenB(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) { - FIXME("\n"); - return E_NOTIMPL; + DWORD len; + HRESULT hres; + + TRACE("%s\n", debugstr_variant(arg)); + + if(V_VT(arg) == VT_NULL) + return return_null(res); + + if(V_VT(arg) != VT_BSTR) { + BSTR str; + + hres = to_string(arg, &str); + if(FAILED(hres)) + return hres; + + len = SysStringByteLen(str); + SysFreeString(str); + }else { + len = SysStringByteLen(V_BSTR(arg)); + } + + return return_int(res, len); } static HRESULT Global_Left(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res) @@ -1340,10 +1360,49 @@ static HRESULT Global_Left(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, return return_bstr(res, ret); } -static HRESULT Global_LeftB(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +static HRESULT Global_LeftB(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res) { - FIXME("\n"); - return E_NOTIMPL; + BSTR str, conv_str = NULL; + int len, byte_len; + HRESULT hres; + + TRACE("(%s %s)\n", debugstr_variant(args+1), debugstr_variant(args)); + + if(V_VT(args) == VT_BSTR) { + str = V_BSTR(args); + }else { + hres = to_string(args, &conv_str); + if(FAILED(hres)) + return hres; + str = conv_str; + } + + hres = to_int(args+1, &len); + if(FAILED(hres)) { + SysFreeString(conv_str); + return hres; + } + + if(len < 0) { + SysFreeString(conv_str); + return MAKE_VBSERROR(VBSE_ILLEGAL_FUNC_CALL); + } + + byte_len = SysStringByteLen(str); + if(len > byte_len) + len = byte_len; + + if(res) { + V_VT(res) = VT_BSTR; + V_BSTR(res) = SysAllocStringByteLen((const char*)str, len); + if(!V_BSTR(res)) { + SysFreeString(conv_str); + return E_OUTOFMEMORY; + } + } + + SysFreeString(conv_str); + return S_OK; } static HRESULT Global_Right(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res) @@ -1388,10 +1447,51 @@ static HRESULT Global_Right(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, return return_bstr(res, ret); } -static HRESULT Global_RightB(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +static HRESULT Global_RightB(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res) { - FIXME("\n"); - return E_NOTIMPL; + BSTR str, conv_str = NULL; + int len, byte_len; + HRESULT hres; + + TRACE("(%s %s)\n", debugstr_variant(args), debugstr_variant(args+1)); + + if(V_VT(args+1) == VT_NULL) + return MAKE_VBSERROR(VBSE_ILLEGAL_NULL_USE); + + hres = to_int(args+1, &len); + if(FAILED(hres)) + return hres; + + if(len < 0) + return MAKE_VBSERROR(VBSE_ILLEGAL_FUNC_CALL); + + if(V_VT(args) == VT_NULL) + return return_null(res); + + if(V_VT(args) == VT_BSTR) { + str = V_BSTR(args); + }else { + hres = to_string(args, &conv_str); + if(FAILED(hres)) + return hres; + str = conv_str; + } + + byte_len = SysStringByteLen(str); + if(len > byte_len) + len = byte_len; + + if(res) { + V_VT(res) = VT_BSTR; + V_BSTR(res) = SysAllocStringByteLen((const char*)str + byte_len - len, len); + if(!V_BSTR(res)) { + SysFreeString(conv_str); + return E_OUTOFMEMORY; + } + } + + SysFreeString(conv_str); + return S_OK; } static HRESULT Global_Mid(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res) @@ -1466,10 +1566,75 @@ static HRESULT Global_Mid(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, V return hres; } -static HRESULT Global_MidB(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +static HRESULT Global_MidB(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res) { - FIXME("\n"); - return E_NOTIMPL; + int len = -1, start, byte_len; + BSTR str, conv_str = NULL; + HRESULT hres; + + TRACE("(%s %s ...)\n", debugstr_variant(args), debugstr_variant(args+1)); + + assert(args_cnt == 2 || args_cnt == 3); + + if(V_VT(args+1) == VT_NULL || (args_cnt == 3 && V_VT(args+2) == VT_NULL)) + return MAKE_VBSERROR(VBSE_ILLEGAL_NULL_USE); + + if(V_VT(args+1) == VT_EMPTY) + return MAKE_VBSERROR(VBSE_ILLEGAL_FUNC_CALL); + + hres = to_int(args+1, &start); + if(FAILED(hres)) + return hres; + + if(start <= 0) + return MAKE_VBSERROR(VBSE_ILLEGAL_FUNC_CALL); + + if(args_cnt == 3) { + if(V_VT(args+2) == VT_EMPTY) + return MAKE_VBSERROR(VBSE_ILLEGAL_FUNC_CALL); + + hres = to_int(args+2, &len); + if(FAILED(hres)) + return hres; + + if(len < 0) + return MAKE_VBSERROR(VBSE_ILLEGAL_FUNC_CALL); + } + + if(V_VT(args) == VT_EMPTY) + return return_string(res, L""); + + if(V_VT(args) == VT_NULL) + return return_null(res); + + if(V_VT(args) == VT_BSTR) { + str = V_BSTR(args); + }else { + hres = to_string(args, &conv_str); + if(FAILED(hres)) + return hres; + str = conv_str; + } + + byte_len = SysStringByteLen(str); + start--; + if(start > byte_len) + start = byte_len; + + if(len == -1) + len = byte_len - start; + else if(len > byte_len - start) + len = byte_len - start; + + if(res) { + V_VT(res) = VT_BSTR; + V_BSTR(res) = SysAllocStringByteLen((const char*)str + start, len); + if(!V_BSTR(res)) + hres = E_OUTOFMEMORY; + } + + SysFreeString(conv_str); + return hres; } static HRESULT Global_StrComp(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res) @@ -1809,22 +1974,140 @@ static HRESULT Global_InStr(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, return return_int(res, ++ret ? ret+start : 0); } -static HRESULT Global_InStrB(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +static HRESULT Global_InStrB(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, VARIANT *res) { - FIXME("\n"); - return E_NOTIMPL; + BSTR str1, str2, conv_str1 = NULL, conv_str2 = NULL; + int start = 0, byte_len1, byte_len2; + const char *p, *end; + HRESULT hres; + int ret = -1; + + TRACE("(%u args)\n", args_cnt); + + assert(2 <= args_cnt && args_cnt <= 4); + + if(args_cnt == 2) { + if(V_VT(args) == VT_BSTR) { + str1 = V_BSTR(args); + }else { + hres = to_string(args, &conv_str1); + if(FAILED(hres)) + return hres; + str1 = conv_str1; + } + if(V_VT(args+1) == VT_BSTR) { + str2 = V_BSTR(args+1); + }else { + hres = to_string(args+1, &conv_str2); + if(FAILED(hres)) { + SysFreeString(conv_str1); + return hres; + } + str2 = conv_str2; + } + }else { + hres = to_int(args, &start); + if(FAILED(hres)) + return hres; + if(start <= 0) { + return MAKE_VBSERROR(VBSE_ILLEGAL_FUNC_CALL); + } + start--; + if(V_VT(args+1) == VT_BSTR) { + str1 = V_BSTR(args+1); + }else { + hres = to_string(args+1, &conv_str1); + if(FAILED(hres)) + return hres; + str1 = conv_str1; + } + if(V_VT(args+2) == VT_BSTR) { + str2 = V_BSTR(args+2); + }else { + hres = to_string(args+2, &conv_str2); + if(FAILED(hres)) { + SysFreeString(conv_str1); + return hres; + } + str2 = conv_str2; + } + } + + byte_len1 = SysStringByteLen(str1); + byte_len2 = SysStringByteLen(str2); + + if(byte_len2 == 0) { + ret = start; + }else if(start < byte_len1) { + end = (const char*)str1 + byte_len1 - byte_len2 + 1; + for(p = (const char*)str1 + start; p < end; p++) { + if(!memcmp(p, str2, byte_len2)) { + ret = p - (const char*)str1; + break; + } + } + } + + SysFreeString(conv_str1); + SysFreeString(conv_str2); + return return_int(res, ret >= 0 ? ret + 1 : 0); } static HRESULT Global_AscB(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) { - FIXME("\n"); - return E_NOTIMPL; + BSTR conv_str = NULL, str; + HRESULT hres = S_OK; + + TRACE("(%s)\n", debugstr_variant(arg)); + + switch(V_VT(arg)) { + case VT_NULL: + return MAKE_VBSERROR(VBSE_ILLEGAL_NULL_USE); + case VT_EMPTY: + return MAKE_VBSERROR(VBSE_ILLEGAL_FUNC_CALL); + case VT_BSTR: + str = V_BSTR(arg); + break; + default: + hres = to_string(arg, &conv_str); + if(FAILED(hres)) + return hres; + str = conv_str; + } + + if(!SysStringByteLen(str)) + hres = MAKE_VBSERROR(VBSE_ILLEGAL_FUNC_CALL); + else if(res) { + V_VT(res) = VT_UI1; + V_UI1(res) = *(const BYTE*)str; + } + + SysFreeString(conv_str); + return hres; } static HRESULT Global_ChrB(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) { - FIXME("\n"); - return E_NOTIMPL; + int c; + HRESULT hres; + + TRACE("%s\n", debugstr_variant(arg)); + + hres = to_int(arg, &c); + if(FAILED(hres)) + return hres; + + if(c < 0 || c > 255) + return MAKE_VBSERROR(VBSE_OVERFLOW); + + if(res) { + BYTE b = c; + V_VT(res) = VT_BSTR; + V_BSTR(res) = SysAllocStringByteLen((const char*)&b, 1); + if(!V_BSTR(res)) + return E_OUTOFMEMORY; + } + return S_OK; } static HRESULT Global_Asc(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) @@ -3475,7 +3758,7 @@ static const builtin_prop_t global_props[] = { {L"Hour", Global_Hour, 0, 1}, {L"InputBox", Global_InputBox, 0, 1, 7}, {L"InStr", Global_InStr, 0, 2, 4}, - {L"InStrB", Global_InStrB, 0, 3, 4}, + {L"InStrB", Global_InStrB, 0, 2, 4}, {L"InStrRev", Global_InStrRev, 0, 2, 4}, {L"Int", Global_Int, 0, 1}, {L"IsArray", Global_IsArray, 0, 1}, diff --git a/dlls/vbscript/tests/api.vbs b/dlls/vbscript/tests/api.vbs index e18d887306b..69ffb9e05d9 100644 --- a/dlls/vbscript/tests/api.vbs +++ b/dlls/vbscript/tests/api.vbs @@ -2624,4 +2624,79 @@ end sub call testFormatNumber() call testFormatNumberError() +' LenB tests +Call ok(LenB("") = 0, "LenB("""") = " & LenB("")) +Call ok(LenB("A") = 2, "LenB(""A"") = " & LenB("A")) +Call ok(LenB("ABC") = 6, "LenB(""ABC"") = " & LenB("ABC")) +Call ok(LenB("hello") = 10, "LenB(""hello"") = " & LenB("hello")) +Call ok(getVT(LenB("A")) = "VT_I4", "getVT(LenB) = " & getVT(LenB("A"))) +Call ok(IsNull(LenB(Null)), "LenB(Null) should be Null") + +' LeftB tests +Call ok(LeftB("ABC", 0) = "", "LeftB(""ABC"", 0) = """ & LeftB("ABC", 0) & """") +Call ok(LeftB("ABC", 2) = "A", "LeftB(""ABC"", 2) = " & LeftB("ABC", 2)) +Call ok(LeftB("ABC", 4) = "AB", "LeftB(""ABC"", 4) = " & LeftB("ABC", 4)) +Call ok(LeftB("ABC", 6) = "ABC", "LeftB(""ABC"", 6) = " & LeftB("ABC", 6)) +Call ok(LeftB("ABC", 100) = "ABC", "LeftB(""ABC"", 100) = " & LeftB("ABC", 100)) +Call ok(LenB(LeftB("ABC", 3)) = 3, "LenB(LeftB(""ABC"", 3)) = " & LenB(LeftB("ABC", 3))) + +' RightB tests +Call ok(RightB("ABC", 0) = "", "RightB(""ABC"", 0) = """ & RightB("ABC", 0) & """") +Call ok(RightB("ABC", 2) = "C", "RightB(""ABC"", 2) = " & RightB("ABC", 2)) +Call ok(RightB("ABC", 4) = "BC", "RightB(""ABC"", 4) = " & RightB("ABC", 4)) +Call ok(RightB("ABC", 100) = "ABC", "RightB(""ABC"", 100) = " & RightB("ABC", 100)) +Call ok(LenB(RightB("ABC", 3)) = 3, "LenB(RightB(""ABC"", 3)) = " & LenB(RightB("ABC", 3))) + +' MidB tests +Call ok(MidB("ABC", 1, 2) = "A", "MidB(""ABC"", 1, 2) = " & MidB("ABC", 1, 2)) +Call ok(MidB("ABC", 3, 2) = "B", "MidB(""ABC"", 3, 2) = " & MidB("ABC", 3, 2)) +Call ok(MidB("ABC", 1) = "ABC", "MidB(""ABC"", 1) = " & MidB("ABC", 1)) +Call ok(MidB("ABC", 3) = "BC", "MidB(""ABC"", 3) = " & MidB("ABC", 3)) +Call ok(LenB(MidB("ABC", 2, 2)) = 2, "LenB(MidB(""ABC"", 2, 2)) = " & LenB(MidB("ABC", 2, 2))) + +' InStrB tests +Call ok(InStrB("ABC", "B") = 3, "InStrB(""ABC"", ""B"") = " & InStrB("ABC", "B")) +Call ok(InStrB("ABC", "D") = 0, "InStrB(""ABC"", ""D"") = " & InStrB("ABC", "D")) +Call ok(InStrB("ABC", "A") = 1, "InStrB(""ABC"", ""A"") = " & InStrB("ABC", "A")) +Call ok(InStrB("ABCABC", "B") = 3, "InStrB(""ABCABC"", ""B"") = " & InStrB("ABCABC", "B")) + +' AscB tests +Call ok(AscB("A") = 65, "AscB(""A"") = " & AscB("A")) +Call ok(AscB("a") = 97, "AscB(""a"") = " & AscB("a")) +Call ok(AscB("ABC") = 65, "AscB(""ABC"") = " & AscB("ABC")) +Call ok(getVT(AscB("A")) = "VT_UI1", "getVT(AscB) = " & getVT(AscB("A"))) + +' ChrB tests +Call ok(AscB(ChrB(65)) = 65, "AscB(ChrB(65)) roundtrip = " & AscB(ChrB(65))) +Call ok(LenB(ChrB(65)) = 1, "LenB(ChrB(65)) = " & LenB(ChrB(65))) +Call ok(AscB(ChrB(0)) = 0, "AscB(ChrB(0)) = " & AscB(ChrB(0))) +Call ok(AscB(ChrB(255)) = 255, "AscB(ChrB(255)) = " & AscB(ChrB(255))) + +sub testByteFuncErrors() + on error resume next + dim r + + call Err.clear() + r = AscB("") + Call ok(Err.number = 5, "AscB("""") Err.number = " & Err.number) + + call Err.clear() + r = AscB(Null) + Call ok(Err.number = 94, "AscB(Null) Err.number = " & Err.number) + + call Err.clear() + r = ChrB(256) + Call ok(Err.number = 6, "ChrB(256) Err.number = " & Err.number) + + call Err.clear() + r = ChrB(-1) + Call ok(Err.number = 6, "ChrB(-1) Err.number = " & Err.number) + + call Err.clear() + r = LeftB("ABC", -1) + Call ok(Err.number = 5, "LeftB(-1) Err.number = " & Err.number) +end sub + +call testByteFuncErrors() + Call reportSuccess() -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10510
participants (2)
-
Francis De Brabandere -
Francis De Brabandere (@francisdb)