Module: wine Branch: master Commit: 7639a850c93ec95f6edc262aaaa4251331f9315b URL: https://source.winehq.org/git/wine.git/?a=commit;h=7639a850c93ec95f6edc262aa...
Author: Jacek Caban jacek@codeweavers.com Date: Tue Oct 22 15:54:20 2019 +0200
vbscript: Implement RegExp.Replace.
Signed-off-by: Jacek Caban jacek@codeweavers.com Signed-off-by: Alexandre Julliard julliard@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 = {