[PATCH 0/1] MR10506: vbscript: Implement Escape and Unescape functions.
Based on the jscript implementation of the same ECMA-262 B.2.1/B.2.2 algorithm, adapted for VBScript BSTR/VARIANT types. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10506
From: Francis De Brabandere <francisdb@gmail.com> Based on the jscript implementation of the same ECMA-262 B.2.1/B.2.2 algorithm, adapted for VBScript BSTR/VARIANT types. --- dlls/vbscript/global.c | 153 +++++++++++++++++++++++++++++++++++- dlls/vbscript/tests/api.vbs | 51 ++++++++++++ 2 files changed, 200 insertions(+), 4 deletions(-) diff --git a/dlls/vbscript/global.c b/dlls/vbscript/global.c index 1d244b8207f..f68374bb7fb 100644 --- a/dlls/vbscript/global.c +++ b/dlls/vbscript/global.c @@ -3319,16 +3319,161 @@ static HRESULT Global_Round(BuiltinDisp *This, VARIANT *args, unsigned args_cnt, return return_double(res, d); } +/* Check that the character is one of the 69 non-blank characters as defined by ECMA-262 B.2.1 */ +static inline BOOL is_ecma_nonblank(WCHAR c) +{ + return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') + || c == '@' || c == '*' || c == '_' || c == '+' || c == '-' || c == '.' || c == '/'; +} + +static WCHAR int_to_hex(int i) +{ + if(i < 10) return '0' + i; + return 'A' + i - 10; +} + +static int hex_to_int(WCHAR c) +{ + if(c >= '0' && c <= '9') return c - '0'; + if(c >= 'A' && c <= 'F') return c - 'A' + 10; + if(c >= 'a' && c <= 'f') return c - 'a' + 10; + return -1; +} + static HRESULT Global_Escape(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) { - FIXME("\n"); - return E_NOTIMPL; + BSTR conv_str = NULL, str, ret; + const WCHAR *ptr; + DWORD len = 0; + HRESULT hres; + + TRACE("(%s)\n", debugstr_variant(arg)); + + if(V_VT(arg) == VT_NULL) + return MAKE_VBSERROR(VBSE_ILLEGAL_NULL_USE); + + if(V_VT(arg) == VT_BSTR) { + str = V_BSTR(arg); + }else { + hres = to_string(arg, &conv_str); + if(FAILED(hres)) + return hres; + str = conv_str; + } + + for(ptr = str; *ptr; ptr++) { + if(*ptr > 0xff) + len += 6; + else if(is_ecma_nonblank(*ptr)) + len++; + else + len += 3; + } + + ret = SysAllocStringLen(NULL, len); + if(!ret) { + SysFreeString(conv_str); + return E_OUTOFMEMORY; + } + + len = 0; + for(ptr = str; *ptr; ptr++) { + if(*ptr > 0xff) { + ret[len++] = '%'; + ret[len++] = 'u'; + ret[len++] = int_to_hex(*ptr >> 12); + ret[len++] = int_to_hex((*ptr >> 8) & 0xf); + ret[len++] = int_to_hex((*ptr >> 4) & 0xf); + ret[len++] = int_to_hex(*ptr & 0xf); + }else if(is_ecma_nonblank(*ptr)) { + ret[len++] = *ptr; + }else { + ret[len++] = '%'; + ret[len++] = int_to_hex(*ptr >> 4); + ret[len++] = int_to_hex(*ptr & 0xf); + } + } + + SysFreeString(conv_str); + + if(res) { + V_VT(res) = VT_BSTR; + V_BSTR(res) = ret; + }else { + SysFreeString(ret); + } + return S_OK; } static HRESULT Global_Unescape(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) { - FIXME("\n"); - return E_NOTIMPL; + BSTR conv_str = NULL, str, ret; + const WCHAR *ptr; + DWORD len = 0; + HRESULT hres; + + TRACE("(%s)\n", debugstr_variant(arg)); + + if(V_VT(arg) == VT_NULL) + return MAKE_VBSERROR(VBSE_ILLEGAL_NULL_USE); + + if(V_VT(arg) == VT_BSTR) { + str = V_BSTR(arg); + }else { + hres = to_string(arg, &conv_str); + if(FAILED(hres)) + return hres; + str = conv_str; + } + + /* First pass: compute output length */ + for(ptr = str; *ptr; ptr++) { + if(*ptr == '%') { + if(hex_to_int(ptr[1]) != -1 && hex_to_int(ptr[2]) != -1) + ptr += 2; + else if(ptr[1] == 'u' && hex_to_int(ptr[2]) != -1 && hex_to_int(ptr[3]) != -1 + && hex_to_int(ptr[4]) != -1 && hex_to_int(ptr[5]) != -1) + ptr += 5; + } + len++; + } + + ret = SysAllocStringLen(NULL, len); + if(!ret) { + SysFreeString(conv_str); + return E_OUTOFMEMORY; + } + + /* Second pass: decode */ + len = 0; + for(ptr = str; *ptr; ptr++) { + if(*ptr == '%') { + if(hex_to_int(ptr[1]) != -1 && hex_to_int(ptr[2]) != -1) { + ret[len] = (hex_to_int(ptr[1]) << 4) + hex_to_int(ptr[2]); + ptr += 2; + }else if(ptr[1] == 'u' && hex_to_int(ptr[2]) != -1 && hex_to_int(ptr[3]) != -1 + && hex_to_int(ptr[4]) != -1 && hex_to_int(ptr[5]) != -1) { + ret[len] = (hex_to_int(ptr[2]) << 12) + (hex_to_int(ptr[3]) << 8) + + (hex_to_int(ptr[4]) << 4) + hex_to_int(ptr[5]); + ptr += 5; + }else { + ret[len] = *ptr; + } + }else { + ret[len] = *ptr; + } + len++; + } + + SysFreeString(conv_str); + + if(res) { + V_VT(res) = VT_BSTR; + V_BSTR(res) = ret; + }else { + SysFreeString(ret); + } + return S_OK; } static HRESULT Global_Eval(BuiltinDisp *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) diff --git a/dlls/vbscript/tests/api.vbs b/dlls/vbscript/tests/api.vbs index e18d887306b..576f8af4c6d 100644 --- a/dlls/vbscript/tests/api.vbs +++ b/dlls/vbscript/tests/api.vbs @@ -2624,4 +2624,55 @@ end sub call testFormatNumber() call testFormatNumberError() +' Escape tests +Call ok(Escape("hello") = "hello", "Escape(""hello"") = " & Escape("hello")) +Call ok(Escape("hello world") = "hello%20world", "Escape(""hello world"") = " & Escape("hello world")) +Call ok(Escape("@") = "@", "@ should not be escaped") +Call ok(Escape("*") = "*", "* should not be escaped") +Call ok(Escape("_") = "_", "_ should not be escaped") +Call ok(Escape("+") = "+", "+ should not be escaped") +Call ok(Escape("-") = "-", "- should not be escaped") +Call ok(Escape(".") = ".", ". should not be escaped") +Call ok(Escape("/") = "/", "/ should not be escaped") +Call ok(Escape("ABC") = "ABC", "ABC should not be escaped") +Call ok(Escape("abc") = "abc", "abc should not be escaped") +Call ok(Escape("012") = "012", "012 should not be escaped") +Call ok(Escape("<") = "%3C", "< escape = " & Escape("<")) +Call ok(Escape(">") = "%3E", "> escape = " & Escape(">")) +Call ok(Escape(" ") = "%20", "space escape = " & Escape(" ")) +Call ok(Escape("=") = "%3D", "= escape = " & Escape("=")) +Call ok(Escape("&") = "%26", "& escape = " & Escape("&")) +Call ok(Escape(Unescape("%u20AC")) = "%u20AC", "Euro sign roundtrip escape") +Call ok(Escape(Unescape("%u0100")) = "%u0100", "U+0100 roundtrip escape") +Call ok(Escape("") = "", "Escape("""") should be empty") +Call ok(getVT(Escape("test")) = "VT_BSTR", "getVT(Escape) = " & getVT(Escape("test"))) + +' Unescape tests +Call ok(Unescape("hello") = "hello", "Unescape(""hello"") = " & Unescape("hello")) +Call ok(Unescape("hello%20world") = "hello world", "Unescape(""hello%20world"") = " & Unescape("hello%20world")) +Call ok(Unescape("%3C%3E") = "<>", "Unescape(""%3C%3E"") = " & Unescape("%3C%3E")) +Call ok(Unescape("%3c") = "<", "Unescape(""%3c"") lowercase") +Call ok(Unescape("%3C") = "<", "Unescape(""%3C"") uppercase") +Call ok(Unescape("%") = "%", "Unescape(""%"") incomplete") +Call ok(Unescape("%2") = "%2", "Unescape(""%2"") incomplete") +Call ok(Unescape("%2G") = "%2G", "Unescape(""%2G"") invalid hex") +Call ok(Unescape(Escape("hello world!")) = "hello world!", "Roundtrip basic") +Call ok(Unescape("") = "", "Unescape("""") should be empty") +Call ok(getVT(Unescape("test")) = "VT_BSTR", "getVT(Unescape) = " & getVT(Unescape("test"))) + +sub testEscapeError() + on error resume next + dim r + + call Err.clear() + r = Escape(Null) + Call ok(Err.number = 94, "Escape(Null) Err.number = " & Err.number) + + call Err.clear() + r = Unescape(Null) + Call ok(Err.number = 94, "Unescape(Null) Err.number = " & Err.number) +end sub + +call testEscapeError() + Call reportSuccess() -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10506
This merge request was approved by Jacek Caban. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/10506
participants (3)
-
Francis De Brabandere -
Francis De Brabandere (@francisdb) -
Jacek Caban (@jacek)