storage.dll16: Fix get_nth_next_small_blocknr.
[wine.git] / dlls / jscript / jsregexp.c
blob5798635cb87606c7af9bf6fce83a05253240db96
1 /*
2 * Copyright 2008 Jacek Caban for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #include <math.h>
21 #include "jscript.h"
22 #include "regexp.h"
24 #include "wine/debug.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
28 typedef struct {
29 jsdisp_t dispex;
31 regexp_t *jsregexp;
32 jsstr_t *str;
33 INT last_index;
34 jsval_t last_index_val;
35 } RegExpInstance;
37 static const WCHAR sourceW[] = {'s','o','u','r','c','e',0};
38 static const WCHAR globalW[] = {'g','l','o','b','a','l',0};
39 static const WCHAR ignoreCaseW[] = {'i','g','n','o','r','e','C','a','s','e',0};
40 static const WCHAR multilineW[] = {'m','u','l','t','i','l','i','n','e',0};
41 static const WCHAR lastIndexW[] = {'l','a','s','t','I','n','d','e','x',0};
42 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
43 static const WCHAR execW[] = {'e','x','e','c',0};
44 static const WCHAR testW[] = {'t','e','s','t',0};
46 static const WCHAR leftContextW[] =
47 {'l','e','f','t','C','o','n','t','e','x','t',0};
48 static const WCHAR rightContextW[] =
49 {'r','i','g','h','t','C','o','n','t','e','x','t',0};
51 static const WCHAR idx1W[] = {'$','1',0};
52 static const WCHAR idx2W[] = {'$','2',0};
53 static const WCHAR idx3W[] = {'$','3',0};
54 static const WCHAR idx4W[] = {'$','4',0};
55 static const WCHAR idx5W[] = {'$','5',0};
56 static const WCHAR idx6W[] = {'$','6',0};
57 static const WCHAR idx7W[] = {'$','7',0};
58 static const WCHAR idx8W[] = {'$','8',0};
59 static const WCHAR idx9W[] = {'$','9',0};
61 static inline RegExpInstance *regexp_from_jsdisp(jsdisp_t *jsdisp)
63 return CONTAINING_RECORD(jsdisp, RegExpInstance, dispex);
66 static inline RegExpInstance *regexp_from_vdisp(vdisp_t *vdisp)
68 return regexp_from_jsdisp(vdisp->u.jsdisp);
71 static void set_last_index(RegExpInstance *This, DWORD last_index)
73 This->last_index = last_index;
74 jsval_release(This->last_index_val);
75 This->last_index_val = jsval_number(last_index);
78 static HRESULT do_regexp_match_next(script_ctx_t *ctx, RegExpInstance *regexp,
79 DWORD rem_flags, jsstr_t *jsstr, const WCHAR *str, match_state_t *ret)
81 HRESULT hres;
83 hres = regexp_execute(regexp->jsregexp, ctx, &ctx->tmp_heap,
84 str, jsstr_length(jsstr), ret);
85 if(FAILED(hres))
86 return hres;
87 if(hres == S_FALSE) {
88 if(rem_flags & REM_RESET_INDEX)
89 set_last_index(regexp, 0);
90 return S_FALSE;
93 if(!(rem_flags & REM_NO_CTX_UPDATE) && ctx->last_match != jsstr) {
94 jsstr_release(ctx->last_match);
95 ctx->last_match = jsstr_addref(jsstr);
98 if(!(rem_flags & REM_NO_CTX_UPDATE)) {
99 DWORD i, n = min(sizeof(ctx->match_parens)/sizeof(ctx->match_parens[0]), ret->paren_count);
101 for(i=0; i < n; i++) {
102 if(ret->parens[i].index == -1) {
103 ctx->match_parens[i].index = 0;
104 ctx->match_parens[i].length = 0;
105 }else {
106 ctx->match_parens[i].index = ret->parens[i].index;
107 ctx->match_parens[i].length = ret->parens[i].length;
111 if(n < sizeof(ctx->match_parens)/sizeof(ctx->match_parens[0]))
112 memset(ctx->match_parens+n, 0, sizeof(ctx->match_parens) - n*sizeof(ctx->match_parens[0]));
115 set_last_index(regexp, ret->cp-str);
117 if(!(rem_flags & REM_NO_CTX_UPDATE)) {
118 ctx->last_match_index = ret->cp-str-ret->match_len;
119 ctx->last_match_length = ret->match_len;
122 return S_OK;
125 HRESULT regexp_match_next(script_ctx_t *ctx, jsdisp_t *dispex,
126 DWORD rem_flags, jsstr_t *jsstr, match_state_t **ret)
128 RegExpInstance *regexp = regexp_from_jsdisp(dispex);
129 match_state_t *match;
130 heap_pool_t *mark;
131 const WCHAR *str;
132 HRESULT hres;
134 if((rem_flags & REM_CHECK_GLOBAL) && !(regexp->jsregexp->flags & REG_GLOB)) {
135 if(rem_flags & REM_ALLOC_RESULT)
136 *ret = NULL;
137 return S_FALSE;
140 str = jsstr_flatten(jsstr);
141 if(!str)
142 return E_OUTOFMEMORY;
144 if(rem_flags & REM_ALLOC_RESULT) {
145 match = alloc_match_state(regexp->jsregexp, NULL, str);
146 if(!match)
147 return E_OUTOFMEMORY;
148 *ret = match;
151 mark = heap_pool_mark(&ctx->tmp_heap);
153 if(rem_flags & REM_NO_PARENS) {
154 match = alloc_match_state(regexp->jsregexp, &ctx->tmp_heap, NULL);
155 if(!match) {
156 heap_pool_clear(mark);
157 return E_OUTOFMEMORY;
159 match->cp = (*ret)->cp;
160 match->match_len = (*ret)->match_len;
161 }else {
162 match = *ret;
165 hres = do_regexp_match_next(ctx, regexp, rem_flags, jsstr, str, match);
167 if(rem_flags & REM_NO_PARENS) {
168 (*ret)->cp = match->cp;
169 (*ret)->match_len = match->match_len;
172 heap_pool_clear(mark);
174 if(hres != S_OK && (rem_flags & REM_ALLOC_RESULT)) {
175 heap_free(match);
176 *ret = NULL;
179 return hres;
182 static HRESULT regexp_match(script_ctx_t *ctx, jsdisp_t *dispex, jsstr_t *jsstr, BOOL gflag,
183 match_result_t **match_result, DWORD *result_cnt)
185 RegExpInstance *This = regexp_from_jsdisp(dispex);
186 match_result_t *ret = NULL;
187 match_state_t *result;
188 DWORD i=0, ret_size = 0;
189 heap_pool_t *mark;
190 const WCHAR *str;
191 HRESULT hres;
193 mark = heap_pool_mark(&ctx->tmp_heap);
195 str = jsstr_flatten(jsstr);
196 if(!str)
197 return E_OUTOFMEMORY;
199 result = alloc_match_state(This->jsregexp, &ctx->tmp_heap, str);
200 if(!result) {
201 heap_pool_clear(mark);
202 return E_OUTOFMEMORY;
205 while(1) {
206 hres = do_regexp_match_next(ctx, This, 0, jsstr, str, result);
207 if(hres == S_FALSE) {
208 hres = S_OK;
209 break;
212 if(FAILED(hres))
213 break;
215 if(ret_size == i) {
216 if(ret) {
217 match_result_t *old_ret = ret;
219 ret = heap_realloc(old_ret, (ret_size <<= 1) * sizeof(match_result_t));
220 if(!ret)
221 heap_free(old_ret);
222 }else {
223 ret = heap_alloc((ret_size=4) * sizeof(match_result_t));
225 if(!ret) {
226 hres = E_OUTOFMEMORY;
227 break;
231 ret[i].index = result->cp - str - result->match_len;
232 ret[i++].length = result->match_len;
234 if(!gflag && !(This->jsregexp->flags & REG_GLOB)) {
235 hres = S_OK;
236 break;
240 heap_pool_clear(mark);
241 if(FAILED(hres)) {
242 heap_free(ret);
243 return hres;
246 *match_result = ret;
247 *result_cnt = i;
248 return S_OK;
251 static HRESULT RegExp_get_source(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
253 TRACE("\n");
255 *r = jsval_string(jsstr_addref(regexp_from_jsdisp(jsthis)->str));
256 return S_OK;
259 static HRESULT RegExp_set_source(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value)
261 FIXME("\n");
262 return E_NOTIMPL;
265 static HRESULT RegExp_get_global(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
267 TRACE("\n");
269 *r = jsval_bool(!!(regexp_from_jsdisp(jsthis)->jsregexp->flags & REG_GLOB));
270 return S_OK;
273 static HRESULT RegExp_set_global(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value)
275 FIXME("\n");
276 return E_NOTIMPL;
279 static HRESULT RegExp_get_ignoreCase(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
281 TRACE("\n");
283 *r = jsval_bool(!!(regexp_from_jsdisp(jsthis)->jsregexp->flags & REG_FOLD));
284 return S_OK;
287 static HRESULT RegExp_set_ignoreCase(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value)
289 FIXME("\n");
290 return E_NOTIMPL;
293 static HRESULT RegExp_get_multiline(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
295 TRACE("\n");
297 *r = jsval_bool(!!(regexp_from_jsdisp(jsthis)->jsregexp->flags & REG_MULTILINE));
298 return S_OK;
301 static HRESULT RegExp_set_multiline(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value)
303 FIXME("\n");
304 return E_NOTIMPL;
307 static INT index_from_val(script_ctx_t *ctx, jsval_t v)
309 double n;
310 HRESULT hres;
312 hres = to_number(ctx, v, &n);
313 if(FAILED(hres)) {
314 clear_ei(ctx); /* FIXME: Move ignoring exceptions to to_primitive */
315 return 0;
318 n = floor(n);
319 return is_int32(n) ? n : 0;
322 static HRESULT RegExp_get_lastIndex(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
324 RegExpInstance *regexp = regexp_from_jsdisp(jsthis);
326 TRACE("\n");
328 return jsval_copy(regexp->last_index_val, r);
331 static HRESULT RegExp_set_lastIndex(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t value)
333 RegExpInstance *regexp = regexp_from_jsdisp(jsthis);
334 HRESULT hres;
336 TRACE("\n");
338 jsval_release(regexp->last_index_val);
339 hres = jsval_copy(value, &regexp->last_index_val);
340 if(FAILED(hres))
341 return hres;
343 regexp->last_index = index_from_val(ctx, value);
344 return S_OK;
347 static HRESULT RegExp_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
348 jsval_t *r)
350 RegExpInstance *regexp;
351 unsigned len, f;
352 jsstr_t *ret;
353 WCHAR *ptr;
355 TRACE("\n");
357 if(!is_vclass(jsthis, JSCLASS_REGEXP)) {
358 FIXME("Not a RegExp\n");
359 return E_NOTIMPL;
362 regexp = regexp_from_vdisp(jsthis);
364 if(!r)
365 return S_OK;
367 len = jsstr_length(regexp->str) + 2;
369 f = regexp->jsregexp->flags;
370 if(f & REG_FOLD)
371 len++;
372 if(f & REG_GLOB)
373 len++;
374 if(f & REG_MULTILINE)
375 len++;
377 ret = jsstr_alloc_buf(len, &ptr);
378 if(!ret)
379 return E_OUTOFMEMORY;
381 *ptr++ = '/';
382 ptr += jsstr_flush(regexp->str, ptr);
383 *ptr++ = '/';
385 if(f & REG_FOLD)
386 *ptr++ = 'i';
387 if(f & REG_GLOB)
388 *ptr++ = 'g';
389 if(f & REG_MULTILINE)
390 *ptr++ = 'm';
392 *r = jsval_string(ret);
393 return S_OK;
396 static HRESULT create_match_array(script_ctx_t *ctx, jsstr_t *input_str,
397 const match_state_t *result, IDispatch **ret)
399 const WCHAR *input;
400 jsdisp_t *array;
401 jsstr_t *str;
402 DWORD i;
403 HRESULT hres = S_OK;
405 static const WCHAR indexW[] = {'i','n','d','e','x',0};
406 static const WCHAR inputW[] = {'i','n','p','u','t',0};
407 static const WCHAR lastIndexW[] = {'l','a','s','t','I','n','d','e','x',0};
408 static const WCHAR zeroW[] = {'0',0};
410 input = jsstr_flatten(input_str);
411 if(!input)
412 return E_OUTOFMEMORY;
414 hres = create_array(ctx, result->paren_count+1, &array);
415 if(FAILED(hres))
416 return hres;
418 for(i=0; i < result->paren_count; i++) {
419 if(result->parens[i].index != -1)
420 str = jsstr_substr(input_str, result->parens[i].index, result->parens[i].length);
421 else
422 str = jsstr_empty();
423 if(!str) {
424 hres = E_OUTOFMEMORY;
425 break;
428 hres = jsdisp_propput_idx(array, i+1, jsval_string(str));
429 jsstr_release(str);
430 if(FAILED(hres))
431 break;
434 while(SUCCEEDED(hres)) {
435 hres = jsdisp_propput_name(array, indexW, jsval_number(result->cp-input-result->match_len));
436 if(FAILED(hres))
437 break;
439 hres = jsdisp_propput_name(array, lastIndexW, jsval_number(result->cp-input));
440 if(FAILED(hres))
441 break;
443 hres = jsdisp_propput_name(array, inputW, jsval_string(jsstr_addref(input_str)));
444 if(FAILED(hres))
445 break;
447 str = jsstr_alloc_len(result->cp-result->match_len, result->match_len);
448 if(!str) {
449 hres = E_OUTOFMEMORY;
450 break;
452 hres = jsdisp_propput_name(array, zeroW, jsval_string(str));
453 jsstr_release(str);
454 break;
457 if(FAILED(hres)) {
458 jsdisp_release(array);
459 return hres;
462 *ret = to_disp(array);
463 return S_OK;
466 static HRESULT run_exec(script_ctx_t *ctx, vdisp_t *jsthis, jsval_t arg,
467 jsstr_t **input, match_state_t **result, BOOL *ret)
469 RegExpInstance *regexp;
470 match_state_t *match;
471 DWORD last_index = 0;
472 const WCHAR *string;
473 jsstr_t *jsstr;
474 HRESULT hres;
476 if(!is_vclass(jsthis, JSCLASS_REGEXP)) {
477 FIXME("Not a RegExp\n");
478 return E_NOTIMPL;
481 regexp = regexp_from_vdisp(jsthis);
483 hres = to_flat_string(ctx, arg, &jsstr, &string);
484 if(FAILED(hres))
485 return hres;
487 if(regexp->jsregexp->flags & REG_GLOB) {
488 if(regexp->last_index < 0) {
489 jsstr_release(jsstr);
490 set_last_index(regexp, 0);
491 *ret = FALSE;
492 if(input)
493 *input = jsstr_empty();
494 return S_OK;
497 last_index = regexp->last_index;
500 match = alloc_match_state(regexp->jsregexp, &ctx->tmp_heap, string+last_index);
501 if(!match) {
502 jsstr_release(jsstr);
503 return E_OUTOFMEMORY;
506 hres = regexp_match_next(ctx, &regexp->dispex, REM_RESET_INDEX, jsstr, &match);
507 if(FAILED(hres)) {
508 jsstr_release(jsstr);
509 return hres;
512 *result = match;
513 *ret = hres == S_OK;
514 if(input)
515 *input = jsstr;
516 else
517 jsstr_release(jsstr);
518 return S_OK;
521 static HRESULT RegExp_exec(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
522 jsval_t *r)
524 match_state_t *match;
525 heap_pool_t *mark;
526 BOOL b;
527 jsstr_t *string;
528 HRESULT hres;
530 TRACE("\n");
532 mark = heap_pool_mark(&ctx->tmp_heap);
534 hres = run_exec(ctx, jsthis, argc ? argv[0] : jsval_string(jsstr_empty()), &string, &match, &b);
535 if(FAILED(hres)) {
536 heap_pool_clear(mark);
537 return hres;
540 if(r) {
541 if(b) {
542 IDispatch *ret;
544 hres = create_match_array(ctx, string, match, &ret);
545 if(SUCCEEDED(hres))
546 *r = jsval_disp(ret);
547 }else {
548 *r = jsval_null();
552 heap_pool_clear(mark);
553 jsstr_release(string);
554 return hres;
557 static HRESULT RegExp_test(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
558 jsval_t *r)
560 match_state_t *match;
561 jsstr_t *undef_str;
562 heap_pool_t *mark;
563 BOOL b;
564 HRESULT hres;
566 TRACE("\n");
568 mark = heap_pool_mark(&ctx->tmp_heap);
569 hres = run_exec(ctx, jsthis, argc ? argv[0] : jsval_string(undef_str = jsstr_undefined()), NULL, &match, &b);
570 heap_pool_clear(mark);
571 if(!argc)
572 jsstr_release(undef_str);
573 if(FAILED(hres))
574 return hres;
576 if(r)
577 *r = jsval_bool(b);
578 return S_OK;
581 static HRESULT RegExp_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
582 jsval_t *r)
584 TRACE("\n");
586 switch(flags) {
587 case INVOKE_FUNC:
588 return throw_type_error(ctx, JS_E_FUNCTION_EXPECTED, NULL);
589 default:
590 FIXME("unimplemented flags %x\n", flags);
591 return E_NOTIMPL;
594 return S_OK;
597 static void RegExp_destructor(jsdisp_t *dispex)
599 RegExpInstance *This = regexp_from_jsdisp(dispex);
601 if(This->jsregexp)
602 regexp_destroy(This->jsregexp);
603 jsval_release(This->last_index_val);
604 jsstr_release(This->str);
605 heap_free(This);
608 static const builtin_prop_t RegExp_props[] = {
609 {execW, RegExp_exec, PROPF_METHOD|1},
610 {globalW, NULL,0, RegExp_get_global, RegExp_set_global},
611 {ignoreCaseW, NULL,0, RegExp_get_ignoreCase, RegExp_set_ignoreCase},
612 {lastIndexW, NULL,0, RegExp_get_lastIndex, RegExp_set_lastIndex},
613 {multilineW, NULL,0, RegExp_get_multiline, RegExp_set_multiline},
614 {sourceW, NULL,0, RegExp_get_source, RegExp_set_source},
615 {testW, RegExp_test, PROPF_METHOD|1},
616 {toStringW, RegExp_toString, PROPF_METHOD}
619 static const builtin_info_t RegExp_info = {
620 JSCLASS_REGEXP,
621 {NULL, RegExp_value, 0},
622 sizeof(RegExp_props)/sizeof(*RegExp_props),
623 RegExp_props,
624 RegExp_destructor,
625 NULL
628 static const builtin_prop_t RegExpInst_props[] = {
629 {globalW, NULL,0, RegExp_get_global, RegExp_set_global},
630 {ignoreCaseW, NULL,0, RegExp_get_ignoreCase, RegExp_set_ignoreCase},
631 {lastIndexW, NULL,0, RegExp_get_lastIndex, RegExp_set_lastIndex},
632 {multilineW, NULL,0, RegExp_get_multiline, RegExp_set_multiline},
633 {sourceW, NULL,0, RegExp_get_source, RegExp_set_source}
636 static const builtin_info_t RegExpInst_info = {
637 JSCLASS_REGEXP,
638 {NULL, RegExp_value, 0},
639 sizeof(RegExpInst_props)/sizeof(*RegExpInst_props),
640 RegExpInst_props,
641 RegExp_destructor,
642 NULL
645 static HRESULT alloc_regexp(script_ctx_t *ctx, jsdisp_t *object_prototype, RegExpInstance **ret)
647 RegExpInstance *regexp;
648 HRESULT hres;
650 regexp = heap_alloc_zero(sizeof(RegExpInstance));
651 if(!regexp)
652 return E_OUTOFMEMORY;
654 if(object_prototype)
655 hres = init_dispex(&regexp->dispex, ctx, &RegExp_info, object_prototype);
656 else
657 hres = init_dispex_from_constr(&regexp->dispex, ctx, &RegExpInst_info, ctx->regexp_constr);
659 if(FAILED(hres)) {
660 heap_free(regexp);
661 return hres;
664 *ret = regexp;
665 return S_OK;
668 HRESULT create_regexp(script_ctx_t *ctx, jsstr_t *src, DWORD flags, jsdisp_t **ret)
670 RegExpInstance *regexp;
671 const WCHAR *str;
672 HRESULT hres;
674 TRACE("%s %x\n", debugstr_jsstr(src), flags);
676 str = jsstr_flatten(src);
677 if(!str)
678 return E_OUTOFMEMORY;
680 hres = alloc_regexp(ctx, NULL, &regexp);
681 if(FAILED(hres))
682 return hres;
684 regexp->str = jsstr_addref(src);
685 regexp->last_index_val = jsval_number(0);
687 regexp->jsregexp = regexp_new(ctx, &ctx->tmp_heap, str, jsstr_length(regexp->str), flags, FALSE);
688 if(!regexp->jsregexp) {
689 WARN("regexp_new failed\n");
690 jsdisp_release(&regexp->dispex);
691 return E_FAIL;
694 *ret = &regexp->dispex;
695 return S_OK;
698 HRESULT create_regexp_var(script_ctx_t *ctx, jsval_t src_arg, jsval_t *flags_arg, jsdisp_t **ret)
700 unsigned flags, opt_len = 0;
701 const WCHAR *opt = NULL;
702 jsstr_t *src;
703 HRESULT hres;
705 if(is_object_instance(src_arg)) {
706 jsdisp_t *obj;
708 obj = iface_to_jsdisp(get_object(src_arg));
709 if(obj) {
710 if(is_class(obj, JSCLASS_REGEXP)) {
711 RegExpInstance *regexp = regexp_from_jsdisp(obj);
713 hres = create_regexp(ctx, regexp->str, regexp->jsregexp->flags, ret);
714 jsdisp_release(obj);
715 return hres;
718 jsdisp_release(obj);
722 if(!is_string(src_arg)) {
723 FIXME("src_arg = %s\n", debugstr_jsval(src_arg));
724 return E_NOTIMPL;
727 src = get_string(src_arg);
729 if(flags_arg) {
730 jsstr_t *opt_str;
732 if(!is_string(*flags_arg)) {
733 FIXME("unimplemented for %s\n", debugstr_jsval(*flags_arg));
734 return E_NOTIMPL;
737 opt_str = get_string(*flags_arg);
738 opt = jsstr_flatten(opt_str);
739 if(!opt)
740 return E_OUTOFMEMORY;
741 opt_len = jsstr_length(opt_str);
744 hres = parse_regexp_flags(opt, opt_len, &flags);
745 if(FAILED(hres))
746 return hres;
748 return create_regexp(ctx, src, flags, ret);
751 HRESULT regexp_string_match(script_ctx_t *ctx, jsdisp_t *re, jsstr_t *jsstr, jsval_t *r)
753 static const WCHAR indexW[] = {'i','n','d','e','x',0};
754 static const WCHAR inputW[] = {'i','n','p','u','t',0};
755 static const WCHAR lastIndexW[] = {'l','a','s','t','I','n','d','e','x',0};
757 RegExpInstance *regexp = regexp_from_jsdisp(re);
758 match_result_t *match_result;
759 unsigned match_cnt, i;
760 const WCHAR *str;
761 jsdisp_t *array;
762 HRESULT hres;
764 str = jsstr_flatten(jsstr);
765 if(!str)
766 return E_OUTOFMEMORY;
768 if(!(regexp->jsregexp->flags & REG_GLOB)) {
769 match_state_t *match;
770 heap_pool_t *mark;
772 mark = heap_pool_mark(&ctx->tmp_heap);
773 match = alloc_match_state(regexp->jsregexp, &ctx->tmp_heap, str);
774 if(!match) {
775 heap_pool_clear(mark);
776 return E_OUTOFMEMORY;
779 hres = regexp_match_next(ctx, &regexp->dispex, 0, jsstr, &match);
780 if(FAILED(hres)) {
781 heap_pool_clear(mark);
782 return hres;
785 if(r) {
786 if(hres == S_OK) {
787 IDispatch *ret;
789 hres = create_match_array(ctx, jsstr, match, &ret);
790 if(SUCCEEDED(hres))
791 *r = jsval_disp(ret);
792 }else {
793 *r = jsval_null();
797 heap_pool_clear(mark);
798 return S_OK;
801 hres = regexp_match(ctx, &regexp->dispex, jsstr, FALSE, &match_result, &match_cnt);
802 if(FAILED(hres))
803 return hres;
805 if(!match_cnt) {
806 TRACE("no match\n");
808 if(r)
809 *r = jsval_null();
810 return S_OK;
813 hres = create_array(ctx, match_cnt, &array);
814 if(FAILED(hres))
815 return hres;
817 for(i=0; i < match_cnt; i++) {
818 jsstr_t *tmp_str;
820 tmp_str = jsstr_substr(jsstr, match_result[i].index, match_result[i].length);
821 if(!tmp_str) {
822 hres = E_OUTOFMEMORY;
823 break;
826 hres = jsdisp_propput_idx(array, i, jsval_string(tmp_str));
827 jsstr_release(tmp_str);
828 if(FAILED(hres))
829 break;
832 while(SUCCEEDED(hres)) {
833 hres = jsdisp_propput_name(array, indexW, jsval_number(match_result[match_cnt-1].index));
834 if(FAILED(hres))
835 break;
837 hres = jsdisp_propput_name(array, lastIndexW,
838 jsval_number(match_result[match_cnt-1].index + match_result[match_cnt-1].length));
839 if(FAILED(hres))
840 break;
842 hres = jsdisp_propput_name(array, inputW, jsval_string(jsstr));
843 break;
846 heap_free(match_result);
848 if(SUCCEEDED(hres) && r)
849 *r = jsval_obj(array);
850 else
851 jsdisp_release(array);
852 return hres;
855 static HRESULT global_idx(script_ctx_t *ctx, DWORD idx, jsval_t *r)
857 jsstr_t *ret;
859 ret = jsstr_substr(ctx->last_match, ctx->match_parens[idx].index, ctx->match_parens[idx].length);
860 if(!ret)
861 return E_OUTOFMEMORY;
863 *r = jsval_string(ret);
864 return S_OK;
867 static HRESULT RegExpConstr_get_idx1(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
869 TRACE("\n");
870 return global_idx(ctx, 0, r);
873 static HRESULT RegExpConstr_get_idx2(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
875 TRACE("\n");
876 return global_idx(ctx, 1, r);
879 static HRESULT RegExpConstr_get_idx3(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
881 TRACE("\n");
882 return global_idx(ctx, 2, r);
885 static HRESULT RegExpConstr_get_idx4(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
887 TRACE("\n");
888 return global_idx(ctx, 3, r);
891 static HRESULT RegExpConstr_get_idx5(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
893 TRACE("\n");
894 return global_idx(ctx, 4, r);
897 static HRESULT RegExpConstr_get_idx6(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
899 TRACE("\n");
900 return global_idx(ctx, 5, r);
903 static HRESULT RegExpConstr_get_idx7(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
905 TRACE("\n");
906 return global_idx(ctx, 6, r);
909 static HRESULT RegExpConstr_get_idx8(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
911 TRACE("\n");
912 return global_idx(ctx, 7, r);
915 static HRESULT RegExpConstr_get_idx9(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
917 TRACE("\n");
918 return global_idx(ctx, 8, r);
921 static HRESULT RegExpConstr_get_leftContext(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
923 jsstr_t *ret;
925 TRACE("\n");
927 ret = jsstr_substr(ctx->last_match, 0, ctx->last_match_index);
928 if(!ret)
929 return E_OUTOFMEMORY;
931 *r = jsval_string(ret);
932 return S_OK;
935 static HRESULT RegExpConstr_get_rightContext(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
937 jsstr_t *ret;
939 TRACE("\n");
941 ret = jsstr_substr(ctx->last_match, ctx->last_match_index+ctx->last_match_length,
942 jsstr_length(ctx->last_match) - ctx->last_match_index - ctx->last_match_length);
943 if(!ret)
944 return E_OUTOFMEMORY;
946 *r = jsval_string(ret);
947 return S_OK;
950 static HRESULT RegExpConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv,
951 jsval_t *r)
953 TRACE("\n");
955 switch(flags) {
956 case DISPATCH_METHOD:
957 if(argc) {
958 if(is_object_instance(argv[0])) {
959 jsdisp_t *jsdisp = iface_to_jsdisp(get_object(argv[0]));
960 if(jsdisp) {
961 if(is_class(jsdisp, JSCLASS_REGEXP)) {
962 if(argc > 1 && !is_undefined(argv[1])) {
963 jsdisp_release(jsdisp);
964 return throw_regexp_error(ctx, JS_E_REGEXP_SYNTAX, NULL);
967 if(r)
968 *r = jsval_obj(jsdisp);
969 else
970 jsdisp_release(jsdisp);
971 return S_OK;
973 jsdisp_release(jsdisp);
977 /* fall through */
978 case DISPATCH_CONSTRUCT: {
979 jsdisp_t *ret;
980 HRESULT hres;
982 if(!argc) {
983 FIXME("no args\n");
984 return E_NOTIMPL;
987 hres = create_regexp_var(ctx, argv[0], argc > 1 ? argv+1 : NULL, &ret);
988 if(FAILED(hres))
989 return hres;
991 if(r)
992 *r = jsval_obj(ret);
993 else
994 jsdisp_release(ret);
995 return S_OK;
997 default:
998 FIXME("unimplemented flags: %x\n", flags);
999 return E_NOTIMPL;
1002 return S_OK;
1005 static const builtin_prop_t RegExpConstr_props[] = {
1006 {idx1W, NULL,0, RegExpConstr_get_idx1, builtin_set_const},
1007 {idx2W, NULL,0, RegExpConstr_get_idx2, builtin_set_const},
1008 {idx3W, NULL,0, RegExpConstr_get_idx3, builtin_set_const},
1009 {idx4W, NULL,0, RegExpConstr_get_idx4, builtin_set_const},
1010 {idx5W, NULL,0, RegExpConstr_get_idx5, builtin_set_const},
1011 {idx6W, NULL,0, RegExpConstr_get_idx6, builtin_set_const},
1012 {idx7W, NULL,0, RegExpConstr_get_idx7, builtin_set_const},
1013 {idx8W, NULL,0, RegExpConstr_get_idx8, builtin_set_const},
1014 {idx9W, NULL,0, RegExpConstr_get_idx9, builtin_set_const},
1015 {leftContextW, NULL,0, RegExpConstr_get_leftContext, builtin_set_const},
1016 {rightContextW, NULL,0, RegExpConstr_get_rightContext, builtin_set_const}
1019 static const builtin_info_t RegExpConstr_info = {
1020 JSCLASS_FUNCTION,
1021 DEFAULT_FUNCTION_VALUE,
1022 sizeof(RegExpConstr_props)/sizeof(*RegExpConstr_props),
1023 RegExpConstr_props,
1024 NULL,
1025 NULL
1028 HRESULT create_regexp_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
1030 RegExpInstance *regexp;
1031 HRESULT hres;
1033 static const WCHAR RegExpW[] = {'R','e','g','E','x','p',0};
1035 hres = alloc_regexp(ctx, object_prototype, &regexp);
1036 if(FAILED(hres))
1037 return hres;
1039 hres = create_builtin_constructor(ctx, RegExpConstr_value, RegExpW, &RegExpConstr_info,
1040 PROPF_CONSTR|2, &regexp->dispex, ret);
1042 jsdisp_release(&regexp->dispex);
1043 return hres;
1046 HRESULT parse_regexp_flags(const WCHAR *str, DWORD str_len, DWORD *ret)
1048 const WCHAR *p;
1049 DWORD flags = 0;
1051 for (p = str; p < str+str_len; p++) {
1052 switch (*p) {
1053 case 'g':
1054 flags |= REG_GLOB;
1055 break;
1056 case 'i':
1057 flags |= REG_FOLD;
1058 break;
1059 case 'm':
1060 flags |= REG_MULTILINE;
1061 break;
1062 case 'y':
1063 flags |= REG_STICKY;
1064 break;
1065 default:
1066 WARN("wrong flag %c\n", *p);
1067 return E_FAIL;
1071 *ret = flags;
1072 return S_OK;