jscript: Fixed numeric escapes unescaping.
[wine] / dlls / jscript / string.c
1 /*
2  * Copyright 2008 Jacek Caban for CodeWeavers
3  *
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.
8  *
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.
13  *
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
17  */
18
19 #include "jscript.h"
20
21 #include "wine/debug.h"
22
23 WINE_DEFAULT_DEBUG_CHANNEL(jscript);
24
25 typedef struct {
26     DispatchEx dispex;
27
28     WCHAR *str;
29     DWORD length;
30 } StringInstance;
31
32 static const WCHAR lengthW[] = {'l','e','n','g','t','h',0};
33 static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0};
34 static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0};
35 static const WCHAR anchorW[] = {'a','n','c','h','o','r',0};
36 static const WCHAR bigW[] = {'b','i','g',0};
37 static const WCHAR blinkW[] = {'b','l','i','n','k',0};
38 static const WCHAR boldW[] = {'b','o','l','d',0};
39 static const WCHAR charAtW[] = {'c','h','a','r','A','t',0};
40 static const WCHAR charCodeAtW[] = {'c','h','a','r','C','o','d','e','A','t',0};
41 static const WCHAR concatW[] = {'c','o','n','c','a','t',0};
42 static const WCHAR fixedW[] = {'f','i','x','e','d',0};
43 static const WCHAR fontcolorW[] = {'f','o','n','t','c','o','l','o','r',0};
44 static const WCHAR fontsizeW[] = {'f','o','n','t','s','i','z','e',0};
45 static const WCHAR indexOfW[] = {'i','n','d','e','x','O','f',0};
46 static const WCHAR italicsW[] = {'i','t','a','l','i','c','s',0};
47 static const WCHAR lastIndexOfW[] = {'l','a','s','t','I','n','d','e','x','O','f',0};
48 static const WCHAR linkW[] = {'l','i','n','k',0};
49 static const WCHAR matchW[] = {'m','a','t','c','h',0};
50 static const WCHAR replaceW[] = {'r','e','p','l','a','c','e',0};
51 static const WCHAR searchW[] = {'s','e','a','r','c','h',0};
52 static const WCHAR sliceW[] = {'s','l','i','c','e',0};
53 static const WCHAR smallW[] = {'s','m','a','l','l',0};
54 static const WCHAR splitW[] = {'s','p','l','i','t',0};
55 static const WCHAR strikeW[] = {'s','t','r','i','k','e',0};
56 static const WCHAR subW[] = {'s','u','b',0};
57 static const WCHAR substringW[] = {'s','u','b','s','t','r','i','n','g',0};
58 static const WCHAR substrW[] = {'s','u','b','s','t','r',0};
59 static const WCHAR supW[] = {'s','u','p',0};
60 static const WCHAR toLowerCaseW[] = {'t','o','L','o','w','e','r','C','a','s','e',0};
61 static const WCHAR toUpperCaseW[] = {'t','o','U','p','p','e','r','C','a','s','e',0};
62 static const WCHAR toLocaleLowerCaseW[] = {'t','o','L','o','c','a','l','e','L','o','w','e','r','C','a','s','e',0};
63 static const WCHAR toLocaleUpperCaseW[] = {'t','o','L','o','c','a','l','e','U','p','p','e','r','C','a','s','e',0};
64 static const WCHAR localeCompareW[] = {'l','o','c','a','l','e','C','o','m','p','a','r','e',0};
65 static const WCHAR hasOwnPropertyW[] = {'h','a','s','O','w','n','P','r','o','p','e','r','t','y',0};
66 static const WCHAR propertyIsEnumerableW[] =
67     {'p','r','o','p','e','r','t','y','I','s','E','n','u','m','e','r','a','b','l','e',0};
68 static const WCHAR isPrototypeOfW[] = {'i','s','P','r','o','t','o','t','y','p','e','O','f',0};
69
70 static HRESULT String_length(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
71         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
72 {
73     TRACE("%p\n", dispex);
74
75     switch(flags) {
76     case DISPATCH_PROPERTYGET: {
77         StringInstance *jsthis = (StringInstance*)dispex;
78
79         V_VT(retv) = VT_I4;
80         V_I4(retv) = jsthis->length;
81         break;
82     }
83     default:
84         FIXME("unimplemented flags %x\n", flags);
85         return E_NOTIMPL;
86     }
87
88     return S_OK;
89 }
90
91 /* ECMA-262 3rd Edition    15.5.4.2 */
92 static HRESULT String_toString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
93         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
94 {
95     StringInstance *string;
96
97     TRACE("\n");
98
99     if(!is_class(dispex, JSCLASS_STRING)) {
100         WARN("this is not a string object\n");
101         return E_FAIL;
102     }
103
104     string = (StringInstance*)dispex;
105
106     if(retv) {
107         BSTR str = SysAllocString(string->str);
108         if(!str)
109             return E_OUTOFMEMORY;
110
111         V_VT(retv) = VT_BSTR;
112         V_BSTR(retv) = str;
113     }
114     return S_OK;
115 }
116
117 /* ECMA-262 3rd Edition    15.5.4.2 */
118 static HRESULT String_valueOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
119         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
120 {
121     TRACE("\n");
122
123     return String_toString(dispex, lcid, flags, dp, retv, ei, sp);
124 }
125
126 static HRESULT String_anchor(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
127         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
128 {
129     FIXME("\n");
130     return E_NOTIMPL;
131 }
132
133 static HRESULT String_big(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
134         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
135 {
136     FIXME("\n");
137     return E_NOTIMPL;
138 }
139
140 static HRESULT String_blink(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
141         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
142 {
143     FIXME("\n");
144     return E_NOTIMPL;
145 }
146
147 static HRESULT String_bold(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
148         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
149 {
150     FIXME("\n");
151     return E_NOTIMPL;
152 }
153
154 /* ECMA-262 3rd Edition    15.5.4.5 */
155 static HRESULT String_charAt(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
156         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
157 {
158     StringInstance *strobj;
159     BSTR str;
160     INT pos = 0;
161     HRESULT hres;
162
163     TRACE("\n");
164
165     if(dispex->builtin_info->class != JSCLASS_STRING) {
166         FIXME("not string this not supported\n");
167         return E_NOTIMPL;
168     }
169
170     strobj = (StringInstance*)dispex;
171
172     if(arg_cnt(dp)) {
173         VARIANT num;
174
175         hres = to_integer(dispex->ctx, get_arg(dp, 0), ei, &num);
176         if(FAILED(hres))
177             return hres;
178
179         if(V_VT(&num) == VT_I4) {
180             pos = V_I4(&num);
181         }else {
182             WARN("pos = %lf\n", V_R8(&num));
183             pos = -1;
184         }
185     }
186
187     if(!retv)
188         return S_OK;
189
190     if(0 <= pos && pos < strobj->length)
191         str = SysAllocStringLen(strobj->str+pos, 1);
192     else
193         str = SysAllocStringLen(NULL, 0);
194     if(!str)
195         return E_OUTOFMEMORY;
196
197     V_VT(retv) = VT_BSTR;
198     V_BSTR(retv) = str;
199     return S_OK;
200 }
201
202 /* ECMA-262 3rd Edition    15.5.4.5 */
203 static HRESULT String_charCodeAt(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
204         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
205 {
206     const WCHAR *str;
207     DWORD length, idx = 0;
208     HRESULT hres;
209
210     TRACE("\n");
211
212     if(dispex->builtin_info->class == JSCLASS_STRING) {
213         StringInstance *string = (StringInstance*)dispex;
214
215         str = string->str;
216         length = string->length;
217     }else {
218         FIXME("not string this not supported\n");
219         return E_NOTIMPL;
220     }
221
222     if(arg_cnt(dp) > 0) {
223         VARIANT v;
224
225         hres = to_integer(dispex->ctx, get_arg(dp, 0), ei, &v);
226         if(FAILED(hres))
227             return hres;
228
229         if(V_VT(&v) != VT_I4 || V_I4(&v) < 0 || V_I4(&v) >= length) {
230             FIXME("NAN\n");
231             return E_FAIL;
232         }
233
234         idx = V_I4(&v);
235     }
236
237     if(retv) {
238         V_VT(retv) = VT_I4;
239         V_I4(retv) = str[idx];
240     }
241     return S_OK;
242 }
243
244 /* ECMA-262 3rd Edition    15.5.4.6 */
245 static HRESULT String_concat(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
246         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
247 {
248     BSTR *strs = NULL, ret = NULL;
249     DWORD len = 0, i, l, str_cnt;
250     VARIANT var;
251     WCHAR *ptr;
252     HRESULT hres;
253
254     TRACE("\n");
255
256     str_cnt = arg_cnt(dp)+1;
257     strs = heap_alloc_zero(str_cnt * sizeof(BSTR));
258     if(!strs)
259         return E_OUTOFMEMORY;
260
261     V_VT(&var) = VT_DISPATCH;
262     V_DISPATCH(&var) = (IDispatch*)_IDispatchEx_(dispex);
263
264     hres = to_string(dispex->ctx, &var, ei, strs);
265     if(SUCCEEDED(hres)) {
266         for(i=0; i < arg_cnt(dp); i++) {
267             hres = to_string(dispex->ctx, get_arg(dp, i), ei, strs+i+1);
268             if(FAILED(hres))
269                 break;
270         }
271     }
272
273     if(SUCCEEDED(hres)) {
274         for(i=0; i < str_cnt; i++)
275             len += SysStringLen(strs[i]);
276
277         ptr = ret = SysAllocStringLen(NULL, len);
278
279         for(i=0; i < str_cnt; i++) {
280             l = SysStringLen(strs[i]);
281             memcpy(ptr, strs[i], l*sizeof(WCHAR));
282             ptr += l;
283         }
284     }
285
286     for(i=0; i < str_cnt; i++)
287         SysFreeString(strs[i]);
288     heap_free(strs);
289
290     if(FAILED(hres))
291         return hres;
292
293     if(retv) {
294         V_VT(retv) = VT_BSTR;
295         V_BSTR(retv) = ret;
296     }else {
297         SysFreeString(ret);
298     }
299     return S_OK;
300 }
301
302 static HRESULT String_fixed(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
303         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
304 {
305     FIXME("\n");
306     return E_NOTIMPL;
307 }
308
309 static HRESULT String_fontcolor(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
310         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
311 {
312     FIXME("\n");
313     return E_NOTIMPL;
314 }
315
316 static HRESULT String_fontsize(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
317         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
318 {
319     FIXME("\n");
320     return E_NOTIMPL;
321 }
322
323 static HRESULT String_indexOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
324         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
325 {
326     DWORD length, pos = 0;
327     const WCHAR *str;
328     BSTR search_str;
329     INT ret = -1;
330     HRESULT hres;
331
332     TRACE("\n");
333
334     if(is_class(dispex, JSCLASS_STRING)) {
335         StringInstance *string = (StringInstance*)dispex;
336
337         str = string->str;
338         length = string->length;
339     }else {
340         FIXME("not String this\n");
341         return E_NOTIMPL;
342     }
343
344     if(!arg_cnt(dp)) {
345         if(retv) {
346             V_VT(retv) = VT_I4;
347             V_I4(retv) = -1;
348         }
349         return S_OK;
350     }
351
352     hres = to_string(dispex->ctx, get_arg(dp,0), ei, &search_str);
353     if(FAILED(hres))
354         return hres;
355
356     if(arg_cnt(dp) >= 2) {
357         VARIANT ival;
358
359         hres = to_integer(dispex->ctx, get_arg(dp,1), ei, &ival);
360         if(SUCCEEDED(hres)) {
361             if(V_VT(&ival) == VT_I4)
362                 pos = V_VT(&ival) > 0 ? V_I4(&ival) : 0;
363             else
364                 pos = V_R8(&ival) > 0.0 ? length : 0;
365             if(pos > length)
366                 pos = length;
367         }
368     }
369
370     if(SUCCEEDED(hres)) {
371         const WCHAR *ptr;
372
373         ptr = strstrW(str+pos, search_str);
374         if(ptr)
375             ret = ptr - str;
376         else
377             ret = -1;
378     }
379
380     SysFreeString(search_str);
381     if(FAILED(hres))
382         return hres;
383
384     if(retv) {
385         V_VT(retv) = VT_I4;
386         V_I4(retv) = ret;
387     }
388     return S_OK;
389 }
390
391 static HRESULT String_italics(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
392         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
393 {
394     FIXME("\n");
395     return E_NOTIMPL;
396 }
397
398 static HRESULT String_lastIndexOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
399         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
400 {
401     FIXME("\n");
402     return E_NOTIMPL;
403 }
404
405 static HRESULT String_link(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
406         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
407 {
408     FIXME("\n");
409     return E_NOTIMPL;
410 }
411
412 /* ECMA-262 3rd Edition    15.5.4.10 */
413 static HRESULT String_match(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
414         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
415 {
416     StringInstance *This = (StringInstance*)dispex;
417     match_result_t *match_result;
418     DispatchEx *regexp;
419     DispatchEx *array;
420     VARIANT var, *arg_var;
421     DWORD match_cnt, i;
422     HRESULT hres = S_OK;
423
424     TRACE("\n");
425
426     if(arg_cnt(dp) != 1) {
427         FIXME("unsupported args\n");
428         return E_NOTIMPL;
429     }
430
431     arg_var = get_arg(dp, 0);
432     switch(V_VT(arg_var)) {
433     case VT_DISPATCH:
434         regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg_var));
435         if(regexp) {
436             if(regexp->builtin_info->class == JSCLASS_REGEXP)
437                 break;
438             jsdisp_release(regexp);
439         }
440     default: {
441         BSTR match_str;
442
443         hres = to_string(dispex->ctx, arg_var, ei, &match_str);
444         if(FAILED(hres))
445             return hres;
446
447         hres = create_regexp_str(dispex->ctx, match_str, SysStringLen(match_str), NULL, 0, &regexp);
448         SysFreeString(match_str);
449         if(FAILED(hres))
450             return hres;
451     }
452     }
453
454     hres = regexp_match(regexp, This->str, This->length, FALSE, &match_result, &match_cnt);
455     jsdisp_release(regexp);
456     if(FAILED(hres))
457         return hres;
458
459     if(!match_cnt) {
460         TRACE("no match\n");
461
462         if(retv)
463             V_VT(retv) = VT_NULL;
464         return S_OK;
465     }
466
467     hres = create_array(dispex->ctx, match_cnt, &array);
468     if(FAILED(hres))
469         return hres;
470
471     V_VT(&var) = VT_BSTR;
472
473     for(i=0; i < match_cnt; i++) {
474         V_BSTR(&var) = SysAllocStringLen(match_result[i].str, match_result[i].len);
475         if(!V_BSTR(&var)) {
476             hres = E_OUTOFMEMORY;
477             break;
478         }
479
480         hres = jsdisp_propput_idx(array, i, lcid, &var, ei, NULL/*FIXME*/);
481         SysFreeString(V_BSTR(&var));
482         if(FAILED(hres))
483             break;
484     }
485
486     if(SUCCEEDED(hres) && retv) {
487         V_VT(retv) = VT_DISPATCH;
488         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(array);
489     }else {
490         jsdisp_release(array);
491     }
492     return hres;
493 }
494
495 typedef struct {
496     WCHAR *buf;
497     DWORD size;
498     DWORD len;
499 } strbuf_t;
500
501 static HRESULT strbuf_append(strbuf_t *buf, const WCHAR *str, DWORD len)
502 {
503     if(!len)
504         return S_OK;
505
506     if(len + buf->len > buf->size) {
507         WCHAR *new_buf;
508         DWORD new_size;
509
510         new_size = buf->size ? buf->size<<1 : 16;
511         if(new_size < buf->len+len)
512             new_size = buf->len+len;
513         if(buf->buf)
514             new_buf = heap_realloc(buf->buf, new_size*sizeof(WCHAR));
515         else
516             new_buf = heap_alloc(new_size*sizeof(WCHAR));
517         if(!new_buf)
518             return E_OUTOFMEMORY;
519
520         buf->buf = new_buf;
521         buf->size = new_size;
522     }
523
524     memcpy(buf->buf+buf->len, str, len*sizeof(WCHAR));
525     buf->len += len;
526     return S_OK;
527 }
528
529 static HRESULT rep_call(DispatchEx *func, const WCHAR *str, match_result_t *match, match_result_t *parens,
530         DWORD parens_cnt, LCID lcid, BSTR *ret, jsexcept_t *ei, IServiceProvider *caller)
531 {
532     DISPPARAMS dp = {NULL, NULL, 0, 0};
533     VARIANTARG *args, *arg;
534     VARIANT var;
535     DWORD i;
536     HRESULT hres = S_OK;
537
538     dp.cArgs = parens_cnt+3;
539     dp.rgvarg = args = heap_alloc_zero(sizeof(VARIANT)*dp.cArgs);
540     if(!args)
541         return E_OUTOFMEMORY;
542
543     arg = get_arg(&dp,0);
544     V_VT(arg) = VT_BSTR;
545     V_BSTR(arg) = SysAllocStringLen(match->str, match->len);
546     if(!V_BSTR(arg))
547         hres = E_OUTOFMEMORY;
548
549     if(SUCCEEDED(hres)) {
550         for(i=0; i < parens_cnt; i++) {
551             arg = get_arg(&dp,i+1);
552             V_VT(arg) = VT_BSTR;
553             V_BSTR(arg) = SysAllocStringLen(parens[i].str, parens[i].len);
554             if(!V_BSTR(arg)) {
555                hres = E_OUTOFMEMORY;
556                break;
557             }
558         }
559     }
560
561     if(SUCCEEDED(hres)) {
562         arg = get_arg(&dp,parens_cnt+1);
563         V_VT(arg) = VT_I4;
564         V_I4(arg) = match->str - str;
565
566         arg = get_arg(&dp,parens_cnt+2);
567         V_VT(arg) = VT_BSTR;
568         V_BSTR(arg) = SysAllocString(str);
569         if(!V_BSTR(arg))
570             hres = E_OUTOFMEMORY;
571     }
572
573     if(SUCCEEDED(hres))
574         hres = jsdisp_call_value(func, lcid, DISPATCH_METHOD, &dp, &var, ei, caller);
575
576     for(i=0; i < parens_cnt+1; i++) {
577         if(i != parens_cnt+1)
578             SysFreeString(V_BSTR(get_arg(&dp,i)));
579     }
580     heap_free(args);
581
582     if(FAILED(hres))
583         return hres;
584
585     hres = to_string(func->ctx, &var, ei, ret);
586     VariantClear(&var);
587     return hres;
588 }
589
590 /* ECMA-262 3rd Edition    15.5.4.11 */
591 static HRESULT String_replace(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
592         VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller)
593 {
594     DWORD parens_cnt = 0, parens_size=0, rep_len=0, length;
595     BSTR rep_str = NULL, match_str = NULL, ret_str;
596     DispatchEx *rep_func = NULL, *regexp = NULL;
597     match_result_t *parens = NULL, match;
598     const WCHAR *str;
599     strbuf_t ret = {NULL,0,0};
600     BOOL gcheck = FALSE;
601     VARIANT *arg_var;
602     HRESULT hres = S_OK;
603
604     TRACE("\n");
605
606     if(is_class(dispex, JSCLASS_STRING)) {
607         StringInstance *string = (StringInstance*)dispex;
608         str = string->str;
609         length = string->length;
610     }else {
611         FIXME("not String this\n");
612         return E_NOTIMPL;
613     }
614
615     if(!arg_cnt(dp)) {
616         if(retv) {
617             ret_str = SysAllocString(str);
618             if(!ret_str)
619                 return E_OUTOFMEMORY;
620
621             V_VT(retv) = VT_BSTR;
622             V_BSTR(retv) = ret_str;
623         }
624         return S_OK;
625     }
626
627     arg_var = get_arg(dp, 0);
628     switch(V_VT(arg_var)) {
629     case VT_DISPATCH:
630         regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg_var));
631         if(regexp) {
632             if(is_class(regexp, JSCLASS_REGEXP)) {
633                 break;
634             }else {
635                 jsdisp_release(regexp);
636                 regexp = NULL;
637             }
638         }
639
640     default:
641         hres = to_string(dispex->ctx, arg_var, ei, &match_str);
642         if(FAILED(hres))
643             return hres;
644     }
645
646     if(arg_cnt(dp) >= 2) {
647         arg_var = get_arg(dp,1);
648         switch(V_VT(arg_var)) {
649         case VT_DISPATCH:
650             rep_func = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg_var));
651             if(rep_func) {
652                 if(is_class(rep_func, JSCLASS_FUNCTION)) {
653                     break;
654                 }else {
655                     jsdisp_release(rep_func);
656                     rep_func = NULL;
657                 }
658             }
659
660         default:
661             hres = to_string(dispex->ctx, arg_var, ei, &rep_str);
662             if(FAILED(hres))
663                 break;
664
665             if(strchrW(rep_str, '$')) {
666                 FIXME("unsupported $ in replace string\n");
667                 hres = E_NOTIMPL;
668             }
669
670             rep_len = SysStringLen(rep_str);
671         }
672     }
673
674     if(SUCCEEDED(hres)) {
675         const WCHAR *cp, *ecp;
676
677         cp = ecp = str;
678
679         while(1) {
680             if(regexp) {
681                 hres = regexp_match_next(regexp, gcheck, str, length, &cp, rep_func ? &parens : NULL,
682                                          &parens_size, &parens_cnt, &match);
683                 gcheck = TRUE;
684
685                 if(hres == S_FALSE) {
686                     hres = S_OK;
687                     break;
688                 }
689                 if(FAILED(hres))
690                     break;
691             }else {
692                 match.str = strstrW(cp, match_str);
693                 if(!match.str)
694                     break;
695                 match.len = SysStringLen(match_str);
696                 cp = match.str+match.len;
697             }
698
699             hres = strbuf_append(&ret, ecp, match.str-ecp);
700             ecp = match.str+match.len;
701             if(FAILED(hres))
702                 break;
703
704             if(rep_func) {
705                 BSTR cstr;
706
707                 hres = rep_call(rep_func, str, &match, parens, parens_cnt, lcid, &cstr, ei, caller);
708                 if(FAILED(hres))
709                     break;
710
711                 hres = strbuf_append(&ret, cstr, SysStringLen(cstr));
712                 SysFreeString(cstr);
713                 if(FAILED(hres))
714                     break;
715             }else if(rep_str) {
716                 hres = strbuf_append(&ret, rep_str, rep_len);
717                 if(FAILED(hres))
718                     break;
719             }else {
720                 static const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d'};
721
722                 hres = strbuf_append(&ret, undefinedW, sizeof(undefinedW)/sizeof(WCHAR));
723                 if(FAILED(hres))
724                     break;
725             }
726         }
727
728         if(SUCCEEDED(hres))
729             hres = strbuf_append(&ret, ecp, (str+length)-ecp);
730     }
731
732     if(rep_func)
733         jsdisp_release(rep_func);
734     if(regexp)
735         jsdisp_release(regexp);
736     SysFreeString(rep_str);
737     SysFreeString(match_str);
738     heap_free(parens);
739
740     if(SUCCEEDED(hres) && retv) {
741         ret_str = SysAllocStringLen(ret.buf, ret.len);
742         if(!ret_str)
743             return E_OUTOFMEMORY;
744
745         V_VT(retv) = VT_BSTR;
746         V_BSTR(retv) = ret_str;
747         TRACE("= %s\n", debugstr_w(ret_str));
748     }
749
750     heap_free(ret.buf);
751     return hres;
752 }
753
754 static HRESULT String_search(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
755         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
756 {
757     FIXME("\n");
758     return E_NOTIMPL;
759 }
760
761 /* ECMA-262 3rd Edition    15.5.4.13 */
762 static HRESULT String_slice(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
763         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
764 {
765     const WCHAR *str;
766     DWORD length;
767     INT start=0, end;
768     VARIANT v;
769     HRESULT hres;
770
771     TRACE("\n");
772
773     if(is_class(dispex, JSCLASS_STRING)) {
774         StringInstance *string = (StringInstance*)dispex;
775
776         str = string->str;
777         length = string->length;
778     }else {
779         FIXME("this is not a string class\n");
780         return E_NOTIMPL;
781     }
782
783     if(arg_cnt(dp)) {
784         hres = to_integer(dispex->ctx, dp->rgvarg + dp->cArgs-1, ei, &v);
785         if(FAILED(hres))
786             return hres;
787
788         if(V_VT(&v) == VT_I4) {
789             start = V_I4(&v);
790             if(start < 0) {
791                 start = length + start;
792                 if(start < 0)
793                     start = 0;
794             }else if(start > length) {
795                 start = length;
796             }
797         }else {
798             start = V_R8(&v) < 0.0 ? 0 : length;
799         }
800     }else {
801         start = 0;
802     }
803
804     if(arg_cnt(dp) >= 2) {
805         hres = to_integer(dispex->ctx, dp->rgvarg + dp->cArgs-2, ei, &v);
806         if(FAILED(hres))
807             return hres;
808
809         if(V_VT(&v) == VT_I4) {
810             end = V_I4(&v);
811             if(end < 0) {
812                 end = length + end;
813                 if(end < 0)
814                     end = 0;
815             }else if(end > length) {
816                 end = length;
817             }
818         }else {
819             end = V_R8(&v) < 0.0 ? 0 : length;
820         }
821     }else {
822         end = length;
823     }
824
825     if(end < start)
826         end = start;
827
828     if(retv) {
829         BSTR retstr = SysAllocStringLen(str+start, end-start);
830         if(!str)
831             return E_OUTOFMEMORY;
832
833         V_VT(retv) = VT_BSTR;
834         V_BSTR(retv) = retstr;
835     }
836     return S_OK;
837 }
838
839 static HRESULT String_small(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
840         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
841 {
842     FIXME("\n");
843     return E_NOTIMPL;
844 }
845
846 static HRESULT String_split(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
847         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
848 {
849     StringInstance *string;
850     match_result_t *match_result;
851     DWORD match_cnt, i, len;
852     const WCHAR *ptr;
853     VARIANT *arg, var;
854     DispatchEx *array;
855     HRESULT hres;
856
857     TRACE("\n");
858
859     if(!is_class(dispex, JSCLASS_STRING)) {
860         FIXME("not String this\n");
861         return E_NOTIMPL;
862     }
863
864     string = (StringInstance*)dispex;
865
866     if(arg_cnt(dp) != 1) {
867         FIXME("unsupported args\n");
868         return E_NOTIMPL;
869     }
870
871     arg = get_arg(dp, 0);
872     switch(V_VT(arg)) {
873     case VT_DISPATCH: {
874         DispatchEx *regexp;
875
876         regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg));
877         if(regexp) {
878             if(is_class(regexp, JSCLASS_REGEXP)) {
879                 hres = regexp_match(regexp, string->str, string->length, TRUE, &match_result, &match_cnt);
880                 jsdisp_release(regexp);
881                 if(FAILED(hres))
882                     return hres;
883                 break;
884             }
885             jsdisp_release(regexp);
886         }
887     }
888     default:
889         FIXME("unsupported vt %d\n", V_VT(arg));
890         return E_NOTIMPL;
891     }
892
893     hres = create_array(dispex->ctx, match_cnt+1, &array);
894
895     if(SUCCEEDED(hres)) {
896         ptr = string->str;
897         for(i=0; i < match_cnt; i++) {
898             len = match_result[i].str-ptr;
899             V_VT(&var) = VT_BSTR;
900             V_BSTR(&var) = SysAllocStringLen(ptr, len);
901             if(!V_BSTR(&var)) {
902                 hres = E_OUTOFMEMORY;
903                 break;
904             }
905
906             hres = jsdisp_propput_idx(array, i, lcid, &var, ei, sp);
907             SysFreeString(V_BSTR(&var));
908             if(FAILED(hres))
909                 break;
910
911             ptr = match_result[i].str + match_result[i].len;
912         }
913     }
914
915     if(SUCCEEDED(hres)) {
916         len = (string->str+string->length) - ptr;
917
918         V_VT(&var) = VT_BSTR;
919         V_BSTR(&var) = SysAllocStringLen(ptr, len);
920
921         hres = jsdisp_propput_idx(array, i, lcid, &var, ei, sp);
922         SysFreeString(V_BSTR(&var));
923     }
924
925     heap_free(match_result);
926
927     if(SUCCEEDED(hres) && retv) {
928         V_VT(retv) = VT_DISPATCH;
929         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(array);
930     }else {
931         jsdisp_release(array);
932     }
933
934     return hres;
935 }
936
937 static HRESULT String_strike(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
938         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
939 {
940     FIXME("\n");
941     return E_NOTIMPL;
942 }
943
944 static HRESULT String_sub(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
945         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
946 {
947     FIXME("\n");
948     return E_NOTIMPL;
949 }
950
951 /* ECMA-262 3rd Edition    15.5.4.15 */
952 static HRESULT String_substring(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
953         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
954 {
955     const WCHAR *str;
956     INT start=0, end;
957     DWORD length;
958     VARIANT v;
959     HRESULT hres;
960
961     TRACE("\n");
962
963     if(is_class(dispex, JSCLASS_STRING)) {
964         StringInstance *string = (StringInstance*)dispex;
965
966         length = string->length;
967         str = string->str;
968     }else {
969         FIXME("not string this not supported\n");
970         return E_NOTIMPL;
971     }
972
973     if(arg_cnt(dp) >= 1) {
974         hres = to_integer(dispex->ctx, dp->rgvarg + dp->cArgs-1, ei, &v);
975         if(FAILED(hres))
976             return hres;
977
978         if(V_VT(&v) == VT_I4) {
979             start = V_I4(&v);
980             if(start < 0)
981                 start = 0;
982             else if(start >= length)
983                 start = length;
984         }else {
985             start = V_R8(&v) < 0.0 ? 0 : length;
986         }
987     }
988
989     if(arg_cnt(dp) >= 2) {
990         hres = to_integer(dispex->ctx, dp->rgvarg + dp->cArgs-2, ei, &v);
991         if(FAILED(hres))
992             return hres;
993
994         if(V_VT(&v) == VT_I4) {
995             end = V_I4(&v);
996             if(end < 0)
997                 end = 0;
998             else if(end > length)
999                 end = length;
1000         }else {
1001             end = V_R8(&v) < 0.0 ? 0 : length;
1002         }
1003     }else {
1004         end = length;
1005     }
1006
1007     if(start > end) {
1008         INT tmp = start;
1009         start = end;
1010         end = tmp;
1011     }
1012
1013     if(retv) {
1014         V_VT(retv) = VT_BSTR;
1015         V_BSTR(retv) = SysAllocStringLen(str+start, end-start);
1016         if(!V_BSTR(retv))
1017             return E_OUTOFMEMORY;
1018     }
1019     return S_OK;
1020 }
1021
1022 static HRESULT String_substr(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1023         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1024 {
1025     FIXME("\n");
1026     return E_NOTIMPL;
1027 }
1028
1029 static HRESULT String_sup(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1030         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1031 {
1032     FIXME("\n");
1033     return E_NOTIMPL;
1034 }
1035
1036 static HRESULT String_toLowerCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1037         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1038 {
1039     FIXME("\n");
1040     return E_NOTIMPL;
1041 }
1042
1043 static HRESULT String_toUpperCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1044         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1045 {
1046     FIXME("\n");
1047     return E_NOTIMPL;
1048 }
1049
1050 static HRESULT String_toLocaleLowerCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1051         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1052 {
1053     FIXME("\n");
1054     return E_NOTIMPL;
1055 }
1056
1057 static HRESULT String_toLocaleUpperCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1058         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1059 {
1060     FIXME("\n");
1061     return E_NOTIMPL;
1062 }
1063
1064 static HRESULT String_localeCompare(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1065         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1066 {
1067     FIXME("\n");
1068     return E_NOTIMPL;
1069 }
1070
1071 static HRESULT String_hasOwnProperty(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1072         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1073 {
1074     FIXME("\n");
1075     return E_NOTIMPL;
1076 }
1077
1078 static HRESULT String_propertyIsEnumerable(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1079         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1080 {
1081     FIXME("\n");
1082     return E_NOTIMPL;
1083 }
1084
1085 static HRESULT String_isPrototypeOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1086         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1087 {
1088     FIXME("\n");
1089     return E_NOTIMPL;
1090 }
1091
1092 static HRESULT String_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1093         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1094 {
1095     StringInstance *This = (StringInstance*)dispex;
1096
1097     TRACE("\n");
1098
1099     switch(flags) {
1100     case DISPATCH_PROPERTYGET: {
1101         BSTR str = SysAllocString(This->str);
1102         if(!str)
1103             return E_OUTOFMEMORY;
1104
1105         V_VT(retv) = VT_BSTR;
1106         V_BSTR(retv) = str;
1107         break;
1108     }
1109     default:
1110         FIXME("flags %x\n", flags);
1111         return E_NOTIMPL;
1112     }
1113
1114     return S_OK;
1115 }
1116
1117 static void String_destructor(DispatchEx *dispex)
1118 {
1119     StringInstance *This = (StringInstance*)dispex;
1120
1121     heap_free(This->str);
1122     heap_free(This);
1123 }
1124
1125 static const builtin_prop_t String_props[] = {
1126     {anchorW,                String_anchor,                PROPF_METHOD},
1127     {bigW,                   String_big,                   PROPF_METHOD},
1128     {blinkW,                 String_blink,                 PROPF_METHOD},
1129     {boldW,                  String_bold,                  PROPF_METHOD},
1130     {charAtW,                String_charAt,                PROPF_METHOD},
1131     {charCodeAtW,            String_charCodeAt,            PROPF_METHOD},
1132     {concatW,                String_concat,                PROPF_METHOD},
1133     {fixedW,                 String_fixed,                 PROPF_METHOD},
1134     {fontcolorW,             String_fontcolor,             PROPF_METHOD},
1135     {fontsizeW,              String_fontsize,              PROPF_METHOD},
1136     {hasOwnPropertyW,        String_hasOwnProperty,        PROPF_METHOD},
1137     {indexOfW,               String_indexOf,               PROPF_METHOD},
1138     {isPrototypeOfW,         String_isPrototypeOf,         PROPF_METHOD},
1139     {italicsW,               String_italics,               PROPF_METHOD},
1140     {lastIndexOfW,           String_lastIndexOf,           PROPF_METHOD},
1141     {lengthW,                String_length,                0},
1142     {linkW,                  String_link,                  PROPF_METHOD},
1143     {localeCompareW,         String_localeCompare,         PROPF_METHOD},
1144     {matchW,                 String_match,                 PROPF_METHOD},
1145     {propertyIsEnumerableW,  String_propertyIsEnumerable,  PROPF_METHOD},
1146     {replaceW,               String_replace,               PROPF_METHOD},
1147     {searchW,                String_search,                PROPF_METHOD},
1148     {sliceW,                 String_slice,                 PROPF_METHOD},
1149     {smallW,                 String_small,                 PROPF_METHOD},
1150     {splitW,                 String_split,                 PROPF_METHOD},
1151     {strikeW,                String_strike,                PROPF_METHOD},
1152     {substringW,             String_substring,             PROPF_METHOD},
1153     {substrW,                String_substr,                PROPF_METHOD},
1154     {subW,                   String_sub,                   PROPF_METHOD},
1155     {supW,                   String_sup,                   PROPF_METHOD},
1156     {toLocaleLowerCaseW,     String_toLocaleLowerCase,     PROPF_METHOD},
1157     {toLocaleUpperCaseW,     String_toLocaleUpperCase,     PROPF_METHOD},
1158     {toLowerCaseW,           String_toLowerCase,           PROPF_METHOD},
1159     {toStringW,              String_toString,              PROPF_METHOD},
1160     {toUpperCaseW,           String_toUpperCase,           PROPF_METHOD},
1161     {valueOfW,               String_valueOf,               PROPF_METHOD}
1162 };
1163
1164 static const builtin_info_t String_info = {
1165     JSCLASS_STRING,
1166     {NULL, String_value, 0},
1167     sizeof(String_props)/sizeof(*String_props),
1168     String_props,
1169     String_destructor,
1170     NULL
1171 };
1172
1173 static HRESULT StringConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp,
1174         VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp)
1175 {
1176     HRESULT hres;
1177
1178     TRACE("\n");
1179
1180     switch(flags) {
1181     case INVOKE_FUNC: {
1182         BSTR str;
1183
1184         if(arg_cnt(dp)) {
1185             hres = to_string(dispex->ctx, get_arg(dp, 0), ei, &str);
1186             if(FAILED(hres))
1187                 return hres;
1188         }else {
1189             str = SysAllocStringLen(NULL, 0);
1190             if(!str)
1191                 return E_OUTOFMEMORY;
1192         }
1193
1194         V_VT(retv) = VT_BSTR;
1195         V_BSTR(retv) = str;
1196         break;
1197     }
1198     case DISPATCH_CONSTRUCT: {
1199         DispatchEx *ret;
1200
1201         if(arg_cnt(dp)) {
1202             BSTR str;
1203
1204             hres = to_string(dispex->ctx, get_arg(dp, 0), ei, &str);
1205             if(FAILED(hres))
1206                 return hres;
1207
1208             hres = create_string(dispex->ctx, str, SysStringLen(str), &ret);
1209             SysFreeString(str);
1210         }else {
1211             hres = create_string(dispex->ctx, NULL, 0, &ret);
1212         }
1213
1214         if(FAILED(hres))
1215             return hres;
1216
1217         V_VT(retv) = VT_DISPATCH;
1218         V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(ret);
1219         break;
1220     }
1221
1222     default:
1223         FIXME("unimplemented flags: %x\n", flags);
1224         return E_NOTIMPL;
1225     }
1226
1227     return S_OK;
1228 }
1229
1230 static HRESULT string_alloc(script_ctx_t *ctx, BOOL use_constr, StringInstance **ret)
1231 {
1232     StringInstance *string;
1233     HRESULT hres;
1234
1235     string = heap_alloc_zero(sizeof(StringInstance));
1236     if(!string)
1237         return E_OUTOFMEMORY;
1238
1239     if(use_constr)
1240         hres = init_dispex_from_constr(&string->dispex, ctx, &String_info, ctx->string_constr);
1241     else
1242         hres = init_dispex(&string->dispex, ctx, &String_info, NULL);
1243     if(FAILED(hres)) {
1244         heap_free(string);
1245         return hres;
1246     }
1247
1248     *ret = string;
1249     return S_OK;
1250 }
1251
1252 HRESULT create_string_constr(script_ctx_t *ctx, DispatchEx **ret)
1253 {
1254     StringInstance *string;
1255     HRESULT hres;
1256
1257     hres = string_alloc(ctx, FALSE, &string);
1258     if(FAILED(hres))
1259         return hres;
1260
1261     hres = create_builtin_function(ctx, StringConstr_value, PROPF_CONSTR, &string->dispex, ret);
1262
1263     jsdisp_release(&string->dispex);
1264     return hres;
1265 }
1266
1267 HRESULT create_string(script_ctx_t *ctx, const WCHAR *str, DWORD len, DispatchEx **ret)
1268 {
1269     StringInstance *string;
1270     HRESULT hres;
1271
1272     hres = string_alloc(ctx, TRUE, &string);
1273     if(FAILED(hres))
1274         return hres;
1275
1276     if(len == -1)
1277         len = strlenW(str);
1278
1279     string->length = len;
1280     string->str = heap_alloc((len+1)*sizeof(WCHAR));
1281     if(!string->str) {
1282         jsdisp_release(&string->dispex);
1283         return E_OUTOFMEMORY;
1284     }
1285
1286     memcpy(string->str, str, len*sizeof(WCHAR));
1287     string->str[len] = 0;
1288
1289     *ret = &string->dispex;
1290     return S_OK;
1291
1292 }