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