From: Francis De Brabandere <francisdb@gmail.com> Native VBScript sorts a non-empty BSTR higher than any numeric or boolean operand, regardless of the binary lex order their CStr forms would produce, when the BSTR is either all-whitespace (Space, tab, LF, CR, NBSP, ...) or contains any C0 control character (Chr(0).. Chr(31)). Add an early-out in var_cmp before the CStr-coerce path. BSTRs are length-prefixed and may contain embedded NUL, so the scan walks SysStringLen characters rather than stopping at NUL. --- dlls/vbscript/interp.c | 22 ++++++++++++++++++++++ dlls/vbscript/tests/lang.vbs | 30 +++++++++++++++--------------- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/dlls/vbscript/interp.c b/dlls/vbscript/interp.c index 7ff3baecce8..2ed97da1cce 100644 --- a/dlls/vbscript/interp.c +++ b/dlls/vbscript/interp.c @@ -2168,9 +2168,31 @@ static HRESULT var_cmp(exec_ctx_t *ctx, VARIANT *l, VARIANT *r, unsigned flags) (rvt == VT_BSTR && (is_numeric_vt(lvt) || lvt == VT_BOOL))) { VARIANT *num = lvt == VT_BSTR ? r : l; VARIANT *str = lvt == VT_BSTR ? l : r; + BSTR str_bstr = V_BSTR(str); VARIANT num_str; HRESULT hres; + /* Native treats a BSTR as greater than any numeric or boolean, + * regardless of binary lex order, when it is non-empty and either + * all-whitespace or contains any C0 control character (Chr(0).. + * Chr(31)). BSTRs are length-prefixed and may contain embedded + * NUL, so use SysStringLen rather than NUL-terminated scan. */ + if(str_bstr) { + UINT len = SysStringLen(str_bstr); + BOOL has_ctrl = FALSE, all_space = TRUE; + UINT i; + for(i = 0; i < len; i++) { + if(str_bstr[i] < 0x20) { + has_ctrl = TRUE; + break; + } + if(!iswspace(str_bstr[i])) + all_space = FALSE; + } + if(len && (has_ctrl || all_space)) + return lvt == VT_BSTR ? VARCMP_GT : VARCMP_LT; + } + VariantInit(&num_str); if((V_VT(num) & VT_TYPEMASK) == VT_BOOL) { V_VT(&num_str) = VT_BSTR; diff --git a/dlls/vbscript/tests/lang.vbs b/dlls/vbscript/tests/lang.vbs index 156e2f46103..02e9781b01b 100644 --- a/dlls/vbscript/tests/lang.vbs +++ b/dlls/vbscript/tests/lang.vbs @@ -322,27 +322,27 @@ Dim ws_tab : ws_tab = Chr(9) Dim ws_lf : ws_lf = Chr(10) Dim ws_cr : ws_cr = Chr(13) Dim ws_nbsp : ws_nbsp = Chr(160) -Call todo_wine_ok(not (Len("ab") > ws_space), "Len(""ab"") > Space(3) should be false") -Call todo_wine_ok((Len("ab") < ws_space), "Len(""ab"") < Space(3) should be true") +Call ok(not (Len("ab") > ws_space), "Len(""ab"") > Space(3) should be false") +Call ok((Len("ab") < ws_space), "Len(""ab"") < Space(3) should be true") Call ok(not (Len("ab") = ws_space), "Len(""ab"") = Space(3) should be false") -Call todo_wine_ok(not (Len("ab") > ws_tab), "Len(""ab"") > Chr(9) should be false") -Call todo_wine_ok((Len("ab") < ws_tab), "Len(""ab"") < Chr(9) should be true") -Call todo_wine_ok(not (Len("ab") > ws_lf), "Len(""ab"") > Chr(10) should be false") -Call todo_wine_ok((Len("ab") < ws_lf), "Len(""ab"") < Chr(10) should be true") -Call todo_wine_ok(not (Len("ab") > ws_cr), "Len(""ab"") > Chr(13) should be false") -Call todo_wine_ok((Len("ab") < ws_cr), "Len(""ab"") < Chr(13) should be true") +Call ok(not (Len("ab") > ws_tab), "Len(""ab"") > Chr(9) should be false") +Call ok((Len("ab") < ws_tab), "Len(""ab"") < Chr(9) should be true") +Call ok(not (Len("ab") > ws_lf), "Len(""ab"") > Chr(10) should be false") +Call ok((Len("ab") < ws_lf), "Len(""ab"") < Chr(10) should be true") +Call ok(not (Len("ab") > ws_cr), "Len(""ab"") > Chr(13) should be false") +Call ok((Len("ab") < ws_cr), "Len(""ab"") < Chr(13) should be true") Call ok(not (Len("ab") > ws_nbsp), "Len(""ab"") > Chr(160) should be false") Call ok((Len("ab") < ws_nbsp), "Len(""ab"") < Chr(160) should be true") Dim ctl_nul : ctl_nul = Chr(0) Dim ctl_soh : ctl_soh = Chr(1) Dim ctl_us : ctl_us = Chr(31) -Call todo_wine_ok((Len("ab") < ctl_nul), "Len(""ab"") < Chr(0) should be true") -Call todo_wine_ok((Len("ab") < ctl_soh), "Len(""ab"") < Chr(1) should be true") -Call todo_wine_ok((Len("ab") < ctl_us), "Len(""ab"") < Chr(31) should be true") -Call todo_wine_ok((Len("ab") < (ctl_nul & "5")), "Len(""ab"") < Chr(0)&""5"" should be true") -Call todo_wine_ok((Len("ab") < (ctl_nul & " ")), "Len(""ab"") < Chr(0)&"" "" should be true") -Call todo_wine_ok((Len("ab") < (" " & ctl_nul)), "Len(""ab"") < "" ""&Chr(0) should be true") +Call ok((Len("ab") < ctl_nul), "Len(""ab"") < Chr(0) should be true") +Call ok((Len("ab") < ctl_soh), "Len(""ab"") < Chr(1) should be true") +Call ok((Len("ab") < ctl_us), "Len(""ab"") < Chr(31) should be true") +Call ok((Len("ab") < (ctl_nul & "5")), "Len(""ab"") < Chr(0)&""5"" should be true") +Call ok((Len("ab") < (ctl_nul & " ")), "Len(""ab"") < Chr(0)&"" "" should be true") +Call ok((Len("ab") < (" " & ctl_nul)), "Len(""ab"") < "" ""&Chr(0) should be true") Call ok((Len("ab") > ""), "Len(""ab"") > """" should be true") @@ -356,7 +356,7 @@ End If guard_err = Err.number Err.Clear On Error Goto 0 -Call todo_wine_ok(guard_err = 0, "Len(""ab"") > Space(3) guard should not raise (got " & guard_err & ")") +Call ok(guard_err = 0, "Len(""ab"") > Space(3) guard should not raise (got " & guard_err & ")") ' --- VT_DATE from CDate is non-literal: string compare, no error. --- Dim cdt : cdt = CDate("2024-01-15") -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/10818