Jacek Caban : vbscript: Implement RegExp.Replace.

Alexandre Julliard julliard at winehq.org
Tue Oct 22 16:57:01 CDT 2019


Module: wine
Branch: master
Commit: 7639a850c93ec95f6edc262aaaa4251331f9315b
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=7639a850c93ec95f6edc262aaaa4251331f9315b

Author: Jacek Caban <jacek at codeweavers.com>
Date:   Tue Oct 22 15:54:20 2019 +0200

vbscript: Implement RegExp.Replace.

Signed-off-by: Jacek Caban <jacek at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/vbscript/tests/regexp.vbs |  35 +++++++-
 dlls/vbscript/vbregexp.c       | 177 +++++++++++++++++++++++++++++++++++++++--
 2 files changed, 206 insertions(+), 6 deletions(-)

diff --git a/dlls/vbscript/tests/regexp.vbs b/dlls/vbscript/tests/regexp.vbs
index 834be2101c..6569b98956 100644
--- a/dlls/vbscript/tests/regexp.vbs
+++ b/dlls/vbscript/tests/regexp.vbs
@@ -18,7 +18,7 @@
 
 Option Explicit
 
-Dim x, matches, match, submatch
+Dim x, matches, match, submatch, r
 
 Set x = CreateObject("vbscript.regexp")
 Call ok(getVT(x.Pattern) = "VT_BSTR", "getVT(RegExp.Pattern) = " & getVT(x.Pattern))
@@ -191,4 +191,37 @@ Call ok(match.Value = "", "match.Value = " & match.Value)
 matches = x.test("test")
 Call ok(matches = true, "matches = " & matches)
 
+dim test_global
+
+sub test_replace(pattern, string, rep, exp)
+    dim x, re
+    set re = new regexp
+    re.pattern = pattern
+    re.global = test_global
+    x = re.replace(string, rep)
+    call ok(x = exp, "replace returned " & x & " expected " & exp)
+end sub
+
+test_global = true
+test_replace "xxx", "xxxx", "y", "yx"
+test_replace "\[([^\[]+)\]", "- [test] -", "success", "- success -"
+test_replace "\[([^\[]+)\]", "[test] [test]", "aa", "aa aa"
+test_replace "(\&(\d))", "abc &1 123", "$'", "abc  123 123"
+test_replace "(\&(\d))", "abc &1 123", "$`", "abc abc  123"
+test_replace "(\&(\d))", "abc &1 123", "$3", "abc $3 123"
+test_replace "\[([^\[]+)\]", "- [test] -", true, "- -1 -"
+test_replace "\[([^\[]+)\]", "- [test] -", 6, "- 6 -"
+test_replace "(\$(\d))", "$1,$2", "$$1-$1$2", "$1-$11,$1-$22"
+test_replace "b", "abc", "x$&z", "axbzc"
+
+test_global = false
+test_replace "\[([^\[]+)\]", "[test] [test]", "aa", "aa [test]"
+
+set r = new regexp
+x = r.replace("xxx", "y")
+call ok(x = "yxxx", "x = " & x)
+r.global = true
+x = r.replace("xxx", "y")
+call ok(x = "yxyxyxy", "x = " & x)
+
 Call reportSuccess()
diff --git a/dlls/vbscript/vbregexp.c b/dlls/vbscript/vbregexp.c
index 3400e81066..4edf9dcbfe 100644
--- a/dlls/vbscript/vbregexp.c
+++ b/dlls/vbscript/vbregexp.c
@@ -19,6 +19,7 @@
 #include "vbscript.h"
 #include "regexp.h"
 #include "vbsregexp55.h"
+#include "wchar.h"
 
 #include "wine/debug.h"
 
@@ -1432,13 +1433,179 @@ static HRESULT WINAPI RegExp2_Test(IRegExp2 *iface, BSTR sourceString, VARIANT_B
     return hres;
 }
 
-static HRESULT WINAPI RegExp2_Replace(IRegExp2 *iface, BSTR sourceString,
-        VARIANT replaceVar, BSTR *pDestString)
+typedef struct {
+    WCHAR *buf;
+    DWORD size;
+    DWORD len;
+} strbuf_t;
+
+static BOOL strbuf_ensure_size(strbuf_t *buf, unsigned len)
+{
+    WCHAR *new_buf;
+    DWORD new_size;
+
+    if(len <= buf->size)
+        return TRUE;
+
+    new_size = buf->size ? buf->size<<1 : 16;
+    if(new_size < len)
+        new_size = len;
+    if(buf->buf)
+        new_buf = heap_realloc(buf->buf, new_size*sizeof(WCHAR));
+    else
+        new_buf = heap_alloc(new_size*sizeof(WCHAR));
+    if(!new_buf)
+        return FALSE;
+
+    buf->buf = new_buf;
+    buf->size = new_size;
+    return TRUE;
+}
+
+static HRESULT strbuf_append(strbuf_t *buf, const WCHAR *str, DWORD len)
+{
+    if(!len)
+        return S_OK;
+
+    if(!strbuf_ensure_size(buf, buf->len+len))
+        return E_OUTOFMEMORY;
+
+    memcpy(buf->buf+buf->len, str, len*sizeof(WCHAR));
+    buf->len += len;
+    return S_OK;
+}
+
+static HRESULT WINAPI RegExp2_Replace(IRegExp2 *iface, BSTR source, VARIANT replaceVar, BSTR *ret)
 {
     RegExp2 *This = impl_from_IRegExp2(iface);
-    FIXME("(%p)->(%s %s %p)\n", This, debugstr_w(sourceString),
-            debugstr_variant(&replaceVar), pDestString);
-    return E_NOTIMPL;
+    const WCHAR *cp, *prev_cp = NULL, *ptr, *prev_ptr;
+    size_t match_len = 0, source_len, replace_len;
+    strbuf_t buf = { NULL, 0, 0 };
+    match_state_t *state = NULL;
+    heap_pool_t *mark;
+    VARIANT strv;
+    BSTR replace;
+    HRESULT hres;
+
+    TRACE("(%p)->(%s %s %p)\n", This, debugstr_w(source), debugstr_variant(&replaceVar), ret);
+
+    if(This->pattern) {
+        if(!This->regexp) {
+            This->regexp = regexp_new(NULL, &This->pool, This->pattern,
+                                      lstrlenW(This->pattern), This->flags, FALSE);
+            if(!This->regexp)
+                return E_OUTOFMEMORY;
+        }else {
+            hres = regexp_set_flags(&This->regexp, NULL, &This->pool, This->flags);
+            if(FAILED(hres))
+                return hres;
+        }
+    }
+
+    V_VT(&strv) = VT_EMPTY;
+    hres = VariantChangeType(&strv, &replaceVar, 0, VT_BSTR);
+    if(FAILED(hres))
+        return hres;
+    replace = V_BSTR(&strv);
+    replace_len = SysStringLen(replace);
+    source_len = SysStringLen(source);
+
+    mark = heap_pool_mark(&This->pool);
+    cp = source;
+    if(This->regexp && !(state = alloc_match_state(This->regexp, &This->pool, cp)))
+        hres = E_OUTOFMEMORY;
+
+    while(SUCCEEDED(hres)) {
+        if(This->regexp) {
+            prev_cp = cp;
+            hres = regexp_execute(This->regexp, NULL, &This->pool, source, source_len, state);
+            if(hres != S_OK) break;
+            cp = state->cp;
+            match_len = state->match_len;
+        }else if(prev_cp) {
+            if(cp == source + source_len)
+                break;
+            prev_cp = cp++;
+        }else {
+            prev_cp = cp;
+        }
+
+        hres = strbuf_append(&buf, prev_cp, cp - prev_cp - match_len);
+        if(FAILED(hres))
+            break;
+
+        prev_ptr = replace;
+        while((ptr = wmemchr(prev_ptr, '$', replace + replace_len - prev_ptr))) {
+            hres = strbuf_append(&buf, prev_ptr, ptr - prev_ptr);
+            if(FAILED(hres))
+                break;
+
+            switch(ptr[1]) {
+            case '$':
+                hres = strbuf_append(&buf, ptr, 1);
+                prev_ptr = ptr + 2;
+                break;
+            case '&':
+                hres = strbuf_append(&buf, cp - match_len, match_len);
+                prev_ptr = ptr + 2;
+                break;
+            case '`':
+                hres = strbuf_append(&buf, source, cp - source - match_len);
+                prev_ptr = ptr + 2;
+                break;
+            case '\'':
+                hres = strbuf_append(&buf, cp, source + source_len - cp);
+                prev_ptr = ptr + 2;
+                break;
+            default: {
+                DWORD idx;
+
+                if(!iswdigit(ptr[1])) {
+                    hres = strbuf_append(&buf, ptr, 1);
+                    prev_ptr = ptr + 1;
+                    break;
+                }
+
+                idx = ptr[1] - '0';
+                if(iswdigit(ptr[2]) && idx * 10 + (ptr[2] - '0') <= state->paren_count) {
+                    idx = idx * 10 + (ptr[2] - '0');
+                    prev_ptr = ptr + 3;
+                }else if(idx && idx <= state->paren_count) {
+                    prev_ptr = ptr + 2;
+                }else {
+                    hres = strbuf_append(&buf, ptr, 1);
+                    prev_ptr = ptr + 1;
+                    break;
+                }
+
+                if(state->parens[idx - 1].index != -1)
+                    hres = strbuf_append(&buf, source + state->parens[idx - 1].index,
+                                         state->parens[idx - 1].length);
+                break;
+            }
+            }
+            if(FAILED(hres))
+                break;
+        }
+        if(SUCCEEDED(hres))
+            hres = strbuf_append(&buf, prev_ptr, replace + replace_len - prev_ptr);
+        if(FAILED(hres))
+            break;
+
+        if(!(This->flags & REG_GLOB))
+            break;
+    }
+
+    if(SUCCEEDED(hres)) {
+        hres = strbuf_append(&buf, cp, source + source_len - cp);
+        if(SUCCEEDED(hres) && !(*ret = SysAllocStringLen(buf.buf, buf.len)))
+            hres = E_OUTOFMEMORY;
+    }
+
+    heap_pool_clear(mark);
+    heap_free(buf.buf);
+    SysFreeString(replace);
+    return hres;
 }
 
 static const IRegExp2Vtbl RegExp2Vtbl = {




More information about the wine-cvs mailing list