vbscript: Implement RegExp.Replace.

Signed-off-by: Jacek Caban <jacek@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Jacek Caban 2019-10-22 15:54:20 +02:00 committed by Alexandre Julliard
parent ea5dbc1329
commit 7639a850c9
2 changed files with 206 additions and 6 deletions

View File

@ -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()

View File

@ -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 = {