strmbase: We cannot hold the RenderLock during QualityControlRender_WaitFor.
[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     jsdisp_t 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 fromCharCodeW[] = {'f','r','o','m','C','h','a','r','C','o','d','e',0};
66
67 static inline StringInstance *string_from_vdisp(vdisp_t *vdisp)
68 {
69     return (StringInstance*)vdisp->u.jsdisp;
70 }
71
72 static inline StringInstance *string_this(vdisp_t *jsthis)
73 {
74     return is_vclass(jsthis, JSCLASS_STRING) ? string_from_vdisp(jsthis) : NULL;
75 }
76
77 static HRESULT get_string_val(script_ctx_t *ctx, vdisp_t *jsthis, jsexcept_t *ei,
78         const WCHAR **str, DWORD *len, BSTR *val_str)
79 {
80     StringInstance *string;
81     VARIANT this_var;
82     HRESULT hres;
83
84     if((string = string_this(jsthis))) {
85         *str = string->str;
86         *len = string->length;
87         *val_str = NULL;
88         return S_OK;
89     }
90
91     V_VT(&this_var) = VT_DISPATCH;
92     V_DISPATCH(&this_var) = jsthis->u.disp;
93     hres = to_string(ctx, &this_var, ei, val_str);
94     if(FAILED(hres))
95         return hres;
96
97     *str = *val_str;
98     *len = SysStringLen(*val_str);
99     return S_OK;
100 }
101
102 static HRESULT String_length(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
103         VARIANT *retv, jsexcept_t *ei)
104 {
105     TRACE("%p\n", jsthis);
106
107     switch(flags) {
108     case DISPATCH_PROPERTYGET: {
109         StringInstance *string = string_from_vdisp(jsthis);
110
111         V_VT(retv) = VT_I4;
112         V_I4(retv) = string->length;
113         break;
114     }
115     default:
116         FIXME("unimplemented flags %x\n", flags);
117         return E_NOTIMPL;
118     }
119
120     return S_OK;
121 }
122
123 static HRESULT stringobj_to_string(vdisp_t *jsthis, VARIANT *retv)
124 {
125     StringInstance *string;
126
127     if(!(string = string_this(jsthis))) {
128         WARN("this is not a string object\n");
129         return E_FAIL;
130     }
131
132     if(retv) {
133         BSTR str = SysAllocString(string->str);
134         if(!str)
135             return E_OUTOFMEMORY;
136
137         V_VT(retv) = VT_BSTR;
138         V_BSTR(retv) = str;
139     }
140     return S_OK;
141 }
142
143 /* ECMA-262 3rd Edition    15.5.4.2 */
144 static HRESULT String_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
145         VARIANT *retv, jsexcept_t *ei)
146 {
147     TRACE("\n");
148
149     return stringobj_to_string(jsthis, retv);
150 }
151
152 /* ECMA-262 3rd Edition    15.5.4.2 */
153 static HRESULT String_valueOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
154         VARIANT *retv, jsexcept_t *ei)
155 {
156     TRACE("\n");
157
158     return stringobj_to_string(jsthis, retv);
159 }
160
161 static HRESULT do_attributeless_tag_format(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
162         VARIANT *retv, jsexcept_t *ei, const WCHAR *tagname)
163 {
164     const WCHAR *str;
165     DWORD length;
166     BSTR val_str = NULL;
167     HRESULT hres;
168
169     static const WCHAR tagfmt[] = {'<','%','s','>','%','s','<','/','%','s','>',0};
170
171     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
172     if(FAILED(hres))
173         return hres;
174
175     if(retv) {
176         BSTR ret = SysAllocStringLen(NULL, length + 2*strlenW(tagname) + 5);
177         if(!ret) {
178             SysFreeString(val_str);
179             return E_OUTOFMEMORY;
180         }
181
182         sprintfW(ret, tagfmt, tagname, str, tagname);
183
184         V_VT(retv) = VT_BSTR;
185         V_BSTR(retv) = ret;
186     }
187
188     SysFreeString(val_str);
189     return S_OK;
190 }
191
192 static HRESULT do_attribute_tag_format(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
193         DISPPARAMS *dp, VARIANT *retv, jsexcept_t *ei,
194         const WCHAR *tagname, const WCHAR *attr)
195 {
196     static const WCHAR tagfmtW[]
197         = {'<','%','s',' ','%','s','=','\"','%','s','\"','>','%','s','<','/','%','s','>',0};
198     static const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d',0};
199
200     StringInstance *string;
201     const WCHAR *str;
202     DWORD length;
203     BSTR attr_value, val_str = NULL;
204     HRESULT hres;
205
206     if(!(string = string_this(jsthis))) {
207         VARIANT this;
208
209         V_VT(&this) = VT_DISPATCH;
210         V_DISPATCH(&this) = jsthis->u.disp;
211
212         hres = to_string(ctx, &this, ei, &val_str);
213         if(FAILED(hres))
214             return hres;
215
216         str = val_str;
217         length = SysStringLen(val_str);
218     }
219     else {
220         str = string->str;
221         length = string->length;
222     }
223
224     if(arg_cnt(dp)) {
225         hres = to_string(ctx, get_arg(dp, 0), ei, &attr_value);
226         if(FAILED(hres)) {
227             SysFreeString(val_str);
228             return hres;
229         }
230     }
231     else {
232         attr_value = SysAllocString(undefinedW);
233         if(!attr_value) {
234             SysFreeString(val_str);
235             return E_OUTOFMEMORY;
236         }
237     }
238
239     if(retv) {
240         BSTR ret = SysAllocStringLen(NULL, length + 2*strlenW(tagname)
241                 + strlenW(attr) + SysStringLen(attr_value) + 9);
242         if(!ret) {
243             SysFreeString(attr_value);
244             SysFreeString(val_str);
245             return E_OUTOFMEMORY;
246         }
247
248         sprintfW(ret, tagfmtW, tagname, attr, attr_value, str, tagname);
249
250         V_VT(retv) = VT_BSTR;
251         V_BSTR(retv) = ret;
252     }
253
254     SysFreeString(attr_value);
255     SysFreeString(val_str);
256     return S_OK;
257 }
258
259 static HRESULT String_anchor(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
260         VARIANT *retv, jsexcept_t *ei)
261 {
262     static const WCHAR fontW[] = {'A',0};
263     static const WCHAR colorW[] = {'N','A','M','E',0};
264
265     return do_attribute_tag_format(ctx, jsthis, flags, dp, retv, ei, fontW, colorW);
266 }
267
268 static HRESULT String_big(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
269         VARIANT *retv, jsexcept_t *ei)
270 {
271     static const WCHAR bigtagW[] = {'B','I','G',0};
272     return do_attributeless_tag_format(ctx, jsthis, flags, dp, retv, ei, bigtagW);
273 }
274
275 static HRESULT String_blink(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
276         VARIANT *retv, jsexcept_t *ei)
277 {
278     static const WCHAR blinktagW[] = {'B','L','I','N','K',0};
279     return do_attributeless_tag_format(ctx, jsthis, flags, dp, retv, ei, blinktagW);
280 }
281
282 static HRESULT String_bold(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
283         VARIANT *retv, jsexcept_t *ei)
284 {
285     static const WCHAR boldtagW[] = {'B',0};
286     return do_attributeless_tag_format(ctx, jsthis, flags, dp, retv, ei, boldtagW);
287 }
288
289 /* ECMA-262 3rd Edition    15.5.4.5 */
290 static HRESULT String_charAt(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
291         VARIANT *retv, jsexcept_t *ei)
292 {
293     const WCHAR *str;
294     DWORD length;
295     BSTR ret, val_str;
296     INT pos = 0;
297     HRESULT hres;
298
299     TRACE("\n");
300
301     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
302     if(FAILED(hres))
303         return hres;
304
305     if(arg_cnt(dp)) {
306         VARIANT num;
307
308         hres = to_integer(ctx, get_arg(dp, 0), ei, &num);
309         if(FAILED(hres)) {
310             SysFreeString(val_str);
311             return hres;
312         }
313
314         if(V_VT(&num) == VT_I4) {
315             pos = V_I4(&num);
316         }else {
317             WARN("pos = %lf\n", V_R8(&num));
318             pos = -1;
319         }
320     }
321
322     if(!retv) {
323         SysFreeString(val_str);
324         return S_OK;
325     }
326
327     if(0 <= pos && pos < length)
328         ret = SysAllocStringLen(str+pos, 1);
329     else
330         ret = SysAllocStringLen(NULL, 0);
331     SysFreeString(val_str);
332     if(!ret) {
333         return E_OUTOFMEMORY;
334     }
335
336     V_VT(retv) = VT_BSTR;
337     V_BSTR(retv) = ret;
338     return S_OK;
339 }
340
341 /* ECMA-262 3rd Edition    15.5.4.5 */
342 static HRESULT String_charCodeAt(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
343         VARIANT *retv, jsexcept_t *ei)
344 {
345     const WCHAR *str;
346     BSTR val_str;
347     DWORD length, idx = 0;
348     HRESULT hres;
349
350     TRACE("\n");
351
352     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
353     if(FAILED(hres))
354         return hres;
355
356     if(arg_cnt(dp) > 0) {
357         VARIANT v;
358
359         hres = to_integer(ctx, get_arg(dp, 0), ei, &v);
360         if(FAILED(hres)) {
361             SysFreeString(val_str);
362             return hres;
363         }
364
365         if(V_VT(&v) != VT_I4 || V_I4(&v) < 0 || V_I4(&v) >= length) {
366             if(retv) num_set_nan(&v);
367             SysFreeString(val_str);
368             return S_OK;
369         }
370
371         idx = V_I4(&v);
372     }
373
374     if(retv) {
375         V_VT(retv) = VT_I4;
376         V_I4(retv) = str[idx];
377     }
378
379     SysFreeString(val_str);
380     return S_OK;
381 }
382
383 /* ECMA-262 3rd Edition    15.5.4.6 */
384 static HRESULT String_concat(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
385         VARIANT *retv, jsexcept_t *ei)
386 {
387     BSTR *strs = NULL, ret = NULL;
388     DWORD len = 0, i, l, str_cnt;
389     VARIANT var;
390     WCHAR *ptr;
391     HRESULT hres;
392
393     TRACE("\n");
394
395     str_cnt = arg_cnt(dp)+1;
396     strs = heap_alloc_zero(str_cnt * sizeof(BSTR));
397     if(!strs)
398         return E_OUTOFMEMORY;
399
400     V_VT(&var) = VT_DISPATCH;
401     V_DISPATCH(&var) = jsthis->u.disp;
402
403     hres = to_string(ctx, &var, ei, strs);
404     if(SUCCEEDED(hres)) {
405         for(i=0; i < arg_cnt(dp); i++) {
406             hres = to_string(ctx, get_arg(dp, i), ei, strs+i+1);
407             if(FAILED(hres))
408                 break;
409         }
410     }
411
412     if(SUCCEEDED(hres)) {
413         for(i=0; i < str_cnt; i++)
414             len += SysStringLen(strs[i]);
415
416         ptr = ret = SysAllocStringLen(NULL, len);
417
418         for(i=0; i < str_cnt; i++) {
419             l = SysStringLen(strs[i]);
420             memcpy(ptr, strs[i], l*sizeof(WCHAR));
421             ptr += l;
422         }
423     }
424
425     for(i=0; i < str_cnt; i++)
426         SysFreeString(strs[i]);
427     heap_free(strs);
428
429     if(FAILED(hres))
430         return hres;
431
432     if(retv) {
433         V_VT(retv) = VT_BSTR;
434         V_BSTR(retv) = ret;
435     }else {
436         SysFreeString(ret);
437     }
438     return S_OK;
439 }
440
441 static HRESULT String_fixed(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
442         VARIANT *retv, jsexcept_t *ei)
443 {
444     static const WCHAR fixedtagW[] = {'T','T',0};
445     return do_attributeless_tag_format(ctx, jsthis, flags, dp, retv, ei, fixedtagW);
446 }
447
448 static HRESULT String_fontcolor(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
449         VARIANT *retv, jsexcept_t *ei)
450 {
451     static const WCHAR fontW[] = {'F','O','N','T',0};
452     static const WCHAR colorW[] = {'C','O','L','O','R',0};
453
454     return do_attribute_tag_format(ctx, jsthis, flags, dp, retv, ei, fontW, colorW);
455 }
456
457 static HRESULT String_fontsize(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
458         VARIANT *retv, jsexcept_t *ei)
459 {
460     static const WCHAR fontW[] = {'F','O','N','T',0};
461     static const WCHAR colorW[] = {'S','I','Z','E',0};
462
463     return do_attribute_tag_format(ctx, jsthis, flags, dp, retv, ei, fontW, colorW);
464 }
465
466 static HRESULT String_indexOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
467         VARIANT *retv, jsexcept_t *ei)
468 {
469     DWORD length, pos = 0;
470     const WCHAR *str;
471     BSTR search_str, val_str;
472     INT ret = -1;
473     HRESULT hres;
474
475     TRACE("\n");
476
477     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
478     if(FAILED(hres))
479         return hres;
480
481     if(!arg_cnt(dp)) {
482         if(retv) {
483             V_VT(retv) = VT_I4;
484             V_I4(retv) = -1;
485         }
486         SysFreeString(val_str);
487         return S_OK;
488     }
489
490     hres = to_string(ctx, get_arg(dp,0), ei, &search_str);
491     if(FAILED(hres)) {
492         SysFreeString(val_str);
493         return hres;
494     }
495
496     if(arg_cnt(dp) >= 2) {
497         VARIANT ival;
498
499         hres = to_integer(ctx, get_arg(dp,1), ei, &ival);
500         if(SUCCEEDED(hres)) {
501             if(V_VT(&ival) == VT_I4)
502                 pos = V_VT(&ival) > 0 ? V_I4(&ival) : 0;
503             else
504                 pos = V_R8(&ival) > 0.0 ? length : 0;
505             if(pos > length)
506                 pos = length;
507         }
508     }
509
510     if(SUCCEEDED(hres)) {
511         const WCHAR *ptr;
512
513         ptr = strstrW(str+pos, search_str);
514         if(ptr)
515             ret = ptr - str;
516         else
517             ret = -1;
518     }
519
520     SysFreeString(search_str);
521     SysFreeString(val_str);
522     if(FAILED(hres))
523         return hres;
524
525     if(retv) {
526         V_VT(retv) = VT_I4;
527         V_I4(retv) = ret;
528     }
529     return S_OK;
530 }
531
532 static HRESULT String_italics(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
533         VARIANT *retv, jsexcept_t *ei)
534 {
535     static const WCHAR italicstagW[] = {'I',0};
536     return do_attributeless_tag_format(ctx, jsthis, flags, dp, retv, ei, italicstagW);
537 }
538
539 /* ECMA-262 3rd Edition    15.5.4.8 */
540 static HRESULT String_lastIndexOf(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
541         VARIANT *retv, jsexcept_t *ei)
542 {
543     BSTR search_str, val_str;
544     DWORD length, pos, search_len;
545     const WCHAR *str;
546     INT ret = -1;
547     HRESULT hres;
548
549     TRACE("\n");
550
551     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
552     if(FAILED(hres))
553         return hres;
554
555     if(!arg_cnt(dp)) {
556         if(retv) {
557             V_VT(retv) = VT_I4;
558             V_I4(retv) = -1;
559         }
560         SysFreeString(val_str);
561         return S_OK;
562     }
563
564     hres = to_string(ctx, get_arg(dp,0), ei, &search_str);
565     if(FAILED(hres)) {
566         SysFreeString(val_str);
567         return hres;
568     }
569
570     search_len = SysStringLen(search_str);
571
572     if(arg_cnt(dp) >= 2) {
573         VARIANT ival;
574
575         hres = to_integer(ctx, get_arg(dp,1), ei, &ival);
576         if(SUCCEEDED(hres)) {
577             if(V_VT(&ival) == VT_I4)
578                 pos = V_VT(&ival) > 0 ? V_I4(&ival) : 0;
579             else
580                 pos = V_R8(&ival) > 0.0 ? length : 0;
581             if(pos > length)
582                 pos = length;
583         }
584     }else {
585         pos = length;
586     }
587
588     if(SUCCEEDED(hres) && length >= search_len) {
589         const WCHAR *ptr;
590
591         for(ptr = str+min(pos, length-search_len); ptr >= str; ptr--) {
592             if(!memcmp(ptr, search_str, search_len*sizeof(WCHAR))) {
593                 ret = ptr-str;
594                 break;
595             }
596         }
597     }
598
599     SysFreeString(search_str);
600     SysFreeString(val_str);
601     if(FAILED(hres))
602         return hres;
603
604     if(retv) {
605         V_VT(retv) = VT_I4;
606         V_I4(retv) = ret;
607     }
608     return S_OK;
609 }
610
611 static HRESULT String_link(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
612         VARIANT *retv, jsexcept_t *ei)
613 {
614     static const WCHAR fontW[] = {'A',0};
615     static const WCHAR colorW[] = {'H','R','E','F',0};
616
617     return do_attribute_tag_format(ctx, jsthis, flags, dp, retv, ei, fontW, colorW);
618 }
619
620 /* ECMA-262 3rd Edition    15.5.4.10 */
621 static HRESULT String_match(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
622         VARIANT *retv, jsexcept_t *ei)
623 {
624     const WCHAR *str;
625     jsdisp_t *regexp;
626     VARIANT *arg_var;
627     DWORD length;
628     BSTR val_str = NULL;
629     HRESULT hres = S_OK;
630
631     TRACE("\n");
632
633     if(!arg_cnt(dp)) {
634         if(retv) {
635             V_VT(retv) = VT_NULL;
636         }
637
638         return S_OK;
639     }
640
641     arg_var = get_arg(dp, 0);
642     switch(V_VT(arg_var)) {
643     case VT_DISPATCH:
644         regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg_var));
645         if(regexp) {
646             if(is_class(regexp, JSCLASS_REGEXP))
647                 break;
648             jsdisp_release(regexp);
649         }
650     default: {
651         BSTR match_str;
652
653         hres = to_string(ctx, arg_var, ei, &match_str);
654         if(FAILED(hres))
655             return hres;
656
657         hres = create_regexp(ctx, match_str, SysStringLen(match_str), 0, &regexp);
658         SysFreeString(match_str);
659         if(FAILED(hres))
660             return hres;
661     }
662     }
663
664     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
665     if(SUCCEEDED(hres)) {
666         if(!val_str)
667             val_str = SysAllocStringLen(str, length);
668         if(val_str)
669             hres = regexp_string_match(ctx, regexp, val_str, retv, ei);
670         else
671             hres = E_OUTOFMEMORY;
672     }
673
674     jsdisp_release(regexp);
675     SysFreeString(val_str);
676     return hres;
677 }
678
679 typedef struct {
680     WCHAR *buf;
681     DWORD size;
682     DWORD len;
683 } strbuf_t;
684
685 static HRESULT strbuf_append(strbuf_t *buf, const WCHAR *str, DWORD len)
686 {
687     if(!len)
688         return S_OK;
689
690     if(len + buf->len > buf->size) {
691         WCHAR *new_buf;
692         DWORD new_size;
693
694         new_size = buf->size ? buf->size<<1 : 16;
695         if(new_size < buf->len+len)
696             new_size = buf->len+len;
697         if(buf->buf)
698             new_buf = heap_realloc(buf->buf, new_size*sizeof(WCHAR));
699         else
700             new_buf = heap_alloc(new_size*sizeof(WCHAR));
701         if(!new_buf)
702             return E_OUTOFMEMORY;
703
704         buf->buf = new_buf;
705         buf->size = new_size;
706     }
707
708     memcpy(buf->buf+buf->len, str, len*sizeof(WCHAR));
709     buf->len += len;
710     return S_OK;
711 }
712
713 static HRESULT rep_call(script_ctx_t *ctx, jsdisp_t *func, const WCHAR *str, match_result_t *match,
714         match_result_t *parens, DWORD parens_cnt, BSTR *ret, jsexcept_t *ei)
715 {
716     DISPPARAMS dp = {NULL, NULL, 0, 0};
717     VARIANTARG *args, *arg;
718     VARIANT var;
719     DWORD i;
720     HRESULT hres = S_OK;
721
722     dp.cArgs = parens_cnt+3;
723     dp.rgvarg = args = heap_alloc_zero(sizeof(VARIANT)*dp.cArgs);
724     if(!args)
725         return E_OUTOFMEMORY;
726
727     arg = get_arg(&dp,0);
728     V_VT(arg) = VT_BSTR;
729     V_BSTR(arg) = SysAllocStringLen(match->str, match->len);
730     if(!V_BSTR(arg))
731         hres = E_OUTOFMEMORY;
732
733     if(SUCCEEDED(hres)) {
734         for(i=0; i < parens_cnt; i++) {
735             arg = get_arg(&dp,i+1);
736             V_VT(arg) = VT_BSTR;
737             V_BSTR(arg) = SysAllocStringLen(parens[i].str, parens[i].len);
738             if(!V_BSTR(arg)) {
739                hres = E_OUTOFMEMORY;
740                break;
741             }
742         }
743     }
744
745     if(SUCCEEDED(hres)) {
746         arg = get_arg(&dp,parens_cnt+1);
747         V_VT(arg) = VT_I4;
748         V_I4(arg) = match->str - str;
749
750         arg = get_arg(&dp,parens_cnt+2);
751         V_VT(arg) = VT_BSTR;
752         V_BSTR(arg) = SysAllocString(str);
753         if(!V_BSTR(arg))
754             hres = E_OUTOFMEMORY;
755     }
756
757     if(SUCCEEDED(hres))
758         hres = jsdisp_call_value(func, DISPATCH_METHOD, &dp, &var, ei);
759
760     for(i=0; i < parens_cnt+3; i++) {
761         if(i != parens_cnt+1)
762             SysFreeString(V_BSTR(get_arg(&dp,i)));
763     }
764     heap_free(args);
765
766     if(FAILED(hres))
767         return hres;
768
769     hres = to_string(ctx, &var, ei, ret);
770     VariantClear(&var);
771     return hres;
772 }
773
774 /* ECMA-262 3rd Edition    15.5.4.11 */
775 static HRESULT String_replace(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
776         VARIANT *retv, jsexcept_t *ei)
777 {
778     const WCHAR *str;
779     DWORD parens_cnt = 0, parens_size=0, rep_len=0, length;
780     BSTR rep_str = NULL, match_str = NULL, ret_str, val_str;
781     jsdisp_t *rep_func = NULL, *regexp = NULL;
782     match_result_t *parens = NULL, match = {NULL,0}, **parens_ptr = &parens;
783     strbuf_t ret = {NULL,0,0};
784     DWORD re_flags = REM_NO_CTX_UPDATE;
785     VARIANT *arg_var;
786     HRESULT hres = S_OK;
787
788     TRACE("\n");
789
790     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
791     if(FAILED(hres))
792         return hres;
793
794     if(!arg_cnt(dp)) {
795         if(retv) {
796             if(!val_str) {
797                 val_str = SysAllocStringLen(str, length);
798                 if(!val_str)
799                     return E_OUTOFMEMORY;
800             }
801
802             V_VT(retv) = VT_BSTR;
803             V_BSTR(retv) = val_str;
804         }
805         return S_OK;
806     }
807
808     arg_var = get_arg(dp, 0);
809     switch(V_VT(arg_var)) {
810     case VT_DISPATCH:
811         regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg_var));
812         if(regexp) {
813             if(is_class(regexp, JSCLASS_REGEXP)) {
814                 break;
815             }else {
816                 jsdisp_release(regexp);
817                 regexp = NULL;
818             }
819         }
820
821     default:
822         hres = to_string(ctx, arg_var, ei, &match_str);
823         if(FAILED(hres)) {
824             SysFreeString(val_str);
825             return hres;
826         }
827     }
828
829     if(arg_cnt(dp) >= 2) {
830         arg_var = get_arg(dp,1);
831         switch(V_VT(arg_var)) {
832         case VT_DISPATCH:
833             rep_func = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg_var));
834             if(rep_func) {
835                 if(is_class(rep_func, JSCLASS_FUNCTION)) {
836                     break;
837                 }else {
838                     jsdisp_release(rep_func);
839                     rep_func = NULL;
840                 }
841             }
842
843         default:
844             hres = to_string(ctx, arg_var, ei, &rep_str);
845             if(FAILED(hres))
846                 break;
847
848             rep_len = SysStringLen(rep_str);
849             if(!strchrW(rep_str, '$'))
850                 parens_ptr = NULL;
851         }
852     }
853
854     if(SUCCEEDED(hres)) {
855         const WCHAR *cp, *ecp;
856
857         cp = ecp = str;
858
859         while(1) {
860             if(regexp) {
861                 hres = regexp_match_next(ctx, regexp, re_flags, str, length, &cp, parens_ptr,
862                         &parens_size, &parens_cnt, &match);
863                 re_flags |= REM_CHECK_GLOBAL;
864
865                 if(hres == S_FALSE) {
866                     hres = S_OK;
867                     break;
868                 }
869                 if(FAILED(hres))
870                     break;
871
872                 if(!match.len)
873                     cp++;
874             }else {
875                 match.str = strstrW(cp, match_str);
876                 if(!match.str)
877                     break;
878                 match.len = SysStringLen(match_str);
879                 cp = match.str+match.len;
880             }
881
882             hres = strbuf_append(&ret, ecp, match.str-ecp);
883             ecp = match.str+match.len;
884             if(FAILED(hres))
885                 break;
886
887             if(rep_func) {
888                 BSTR cstr;
889
890                 hres = rep_call(ctx, rep_func, str, &match, parens, parens_cnt, &cstr, ei);
891                 if(FAILED(hres))
892                     break;
893
894                 hres = strbuf_append(&ret, cstr, SysStringLen(cstr));
895                 SysFreeString(cstr);
896                 if(FAILED(hres))
897                     break;
898             }else if(rep_str && regexp) {
899                 const WCHAR *ptr = rep_str, *ptr2;
900
901                 while((ptr2 = strchrW(ptr, '$'))) {
902                     hres = strbuf_append(&ret, ptr, ptr2-ptr);
903                     if(FAILED(hres))
904                         break;
905
906                     switch(ptr2[1]) {
907                     case '$':
908                         hres = strbuf_append(&ret, ptr2, 1);
909                         ptr = ptr2+2;
910                         break;
911                     case '&':
912                         hres = strbuf_append(&ret, match.str, match.len);
913                         ptr = ptr2+2;
914                         break;
915                     case '`':
916                         hres = strbuf_append(&ret, str, match.str-str);
917                         ptr = ptr2+2;
918                         break;
919                     case '\'':
920                         hres = strbuf_append(&ret, ecp, (str+length)-ecp);
921                         ptr = ptr2+2;
922                         break;
923                     default: {
924                         DWORD idx;
925
926                         if(!isdigitW(ptr2[1])) {
927                             hres = strbuf_append(&ret, ptr2, 1);
928                             ptr = ptr2+1;
929                             break;
930                         }
931
932                         idx = ptr2[1] - '0';
933                         if(isdigitW(ptr2[2]) && idx*10 + (ptr2[2]-'0') <= parens_cnt) {
934                             idx = idx*10 + (ptr[2]-'0');
935                             ptr = ptr2+3;
936                         }else if(idx && idx <= parens_cnt) {
937                             ptr = ptr2+2;
938                         }else {
939                             hres = strbuf_append(&ret, ptr2, 1);
940                             ptr = ptr2+1;
941                             break;
942                         }
943
944                         hres = strbuf_append(&ret, parens[idx-1].str, parens[idx-1].len);
945                     }
946                     }
947
948                     if(FAILED(hres))
949                         break;
950                 }
951
952                 if(SUCCEEDED(hres))
953                     hres = strbuf_append(&ret, ptr, (rep_str+rep_len)-ptr);
954                 if(FAILED(hres))
955                     break;
956             }else if(rep_str) {
957                 hres = strbuf_append(&ret, rep_str, rep_len);
958                 if(FAILED(hres))
959                     break;
960             }else {
961                 static const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d'};
962
963                 hres = strbuf_append(&ret, undefinedW, sizeof(undefinedW)/sizeof(WCHAR));
964                 if(FAILED(hres))
965                     break;
966             }
967
968             if(!regexp)
969                 break;
970         }
971
972         if(SUCCEEDED(hres))
973             hres = strbuf_append(&ret, ecp, (str+length)-ecp);
974     }
975
976     if(rep_func)
977         jsdisp_release(rep_func);
978     SysFreeString(rep_str);
979     SysFreeString(match_str);
980     heap_free(parens);
981
982     if(SUCCEEDED(hres) && match.str && regexp) {
983         if(!val_str)
984             val_str = SysAllocStringLen(str, length);
985         if(val_str) {
986             SysFreeString(ctx->last_match);
987             ctx->last_match = val_str;
988             val_str = NULL;
989             ctx->last_match_index = match.str-str;
990             ctx->last_match_length = match.len;
991         }else {
992             hres = E_OUTOFMEMORY;
993         }
994     }
995
996     if(regexp)
997         jsdisp_release(regexp);
998     SysFreeString(val_str);
999
1000     if(SUCCEEDED(hres) && retv) {
1001         ret_str = SysAllocStringLen(ret.buf, ret.len);
1002         if(!ret_str)
1003             return E_OUTOFMEMORY;
1004
1005         V_VT(retv) = VT_BSTR;
1006         V_BSTR(retv) = ret_str;
1007         TRACE("= %s\n", debugstr_w(ret_str));
1008     }
1009
1010     heap_free(ret.buf);
1011     return hres;
1012 }
1013
1014 static HRESULT String_search(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
1015         VARIANT *retv, jsexcept_t *ei)
1016 {
1017     jsdisp_t *regexp = NULL;
1018     const WCHAR *str, *cp;
1019     match_result_t match;
1020     VARIANT *arg;
1021     DWORD length;
1022     BSTR val_str;
1023     HRESULT hres;
1024
1025     TRACE("\n");
1026
1027     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
1028     if(FAILED(hres))
1029         return hres;
1030
1031     if(!arg_cnt(dp)) {
1032         if(retv)
1033             V_VT(retv) = VT_NULL;
1034         SysFreeString(val_str);
1035         return S_OK;
1036     }
1037
1038     arg = get_arg(dp,0);
1039     if(V_VT(arg) == VT_DISPATCH) {
1040         regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg));
1041         if(regexp) {
1042             if(!is_class(regexp, JSCLASS_REGEXP)) {
1043                 jsdisp_release(regexp);
1044                 regexp = NULL;
1045             }
1046         }
1047     }
1048
1049     if(!regexp) {
1050         hres = create_regexp_var(ctx, arg, NULL, &regexp);
1051         if(FAILED(hres)) {
1052             SysFreeString(val_str);
1053             return hres;
1054         }
1055     }
1056
1057     cp = str;
1058     hres = regexp_match_next(ctx, regexp, REM_RESET_INDEX, str, length, &cp, NULL, NULL, NULL, &match);
1059     SysFreeString(val_str);
1060     jsdisp_release(regexp);
1061     if(FAILED(hres))
1062         return hres;
1063
1064     if(retv) {
1065         V_VT(retv) = VT_I4;
1066         V_I4(retv) = hres == S_OK ? match.str-str : -1;
1067     }
1068     return S_OK;
1069 }
1070
1071 /* ECMA-262 3rd Edition    15.5.4.13 */
1072 static HRESULT String_slice(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
1073         VARIANT *retv, jsexcept_t *ei)
1074 {
1075     const WCHAR *str;
1076     BSTR val_str;
1077     DWORD length;
1078     INT start=0, end;
1079     VARIANT v;
1080     HRESULT hres;
1081
1082     TRACE("\n");
1083
1084     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
1085     if(FAILED(hres))
1086         return hres;
1087
1088     if(arg_cnt(dp)) {
1089         hres = to_integer(ctx, get_arg(dp,0), ei, &v);
1090         if(FAILED(hres)) {
1091             SysFreeString(val_str);
1092             return hres;
1093         }
1094
1095         if(V_VT(&v) == VT_I4) {
1096             start = V_I4(&v);
1097             if(start < 0) {
1098                 start = length + start;
1099                 if(start < 0)
1100                     start = 0;
1101             }else if(start > length) {
1102                 start = length;
1103             }
1104         }else {
1105             start = V_R8(&v) < 0.0 ? 0 : length;
1106         }
1107     }else {
1108         start = 0;
1109     }
1110
1111     if(arg_cnt(dp) >= 2) {
1112         hres = to_integer(ctx, get_arg(dp,1), ei, &v);
1113         if(FAILED(hres)) {
1114             SysFreeString(val_str);
1115             return hres;
1116         }
1117
1118         if(V_VT(&v) == VT_I4) {
1119             end = V_I4(&v);
1120             if(end < 0) {
1121                 end = length + end;
1122                 if(end < 0)
1123                     end = 0;
1124             }else if(end > length) {
1125                 end = length;
1126             }
1127         }else {
1128             end = V_R8(&v) < 0.0 ? 0 : length;
1129         }
1130     }else {
1131         end = length;
1132     }
1133
1134     if(end < start)
1135         end = start;
1136
1137     if(retv) {
1138         BSTR retstr = SysAllocStringLen(str+start, end-start);
1139         if(!retstr) {
1140             SysFreeString(val_str);
1141             return E_OUTOFMEMORY;
1142         }
1143
1144         V_VT(retv) = VT_BSTR;
1145         V_BSTR(retv) = retstr;
1146     }
1147
1148     SysFreeString(val_str);
1149     return S_OK;
1150 }
1151
1152 static HRESULT String_small(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
1153         VARIANT *retv, jsexcept_t *ei)
1154 {
1155     static const WCHAR smalltagW[] = {'S','M','A','L','L',0};
1156     return do_attributeless_tag_format(ctx, jsthis, flags, dp, retv, ei, smalltagW);
1157 }
1158
1159 static HRESULT String_split(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
1160         VARIANT *retv, jsexcept_t *ei)
1161 {
1162     match_result_t *match_result = NULL;
1163     DWORD length, match_cnt, i, match_len = 0;
1164     const WCHAR *str, *ptr, *ptr2;
1165     BOOL use_regexp = FALSE;
1166     VARIANT *arg, var;
1167     jsdisp_t *array;
1168     BSTR val_str, match_str = NULL;
1169     HRESULT hres;
1170
1171     TRACE("\n");
1172
1173     if(arg_cnt(dp) != 1) {
1174         FIXME("unsupported args\n");
1175         return E_NOTIMPL;
1176     }
1177
1178     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
1179     if(FAILED(hres))
1180         return hres;
1181
1182     arg = get_arg(dp, 0);
1183     switch(V_VT(arg)) {
1184     case VT_DISPATCH: {
1185         jsdisp_t *regexp;
1186
1187         regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg));
1188         if(regexp) {
1189             if(is_class(regexp, JSCLASS_REGEXP)) {
1190                 use_regexp = TRUE;
1191                 hres = regexp_match(ctx, regexp, str, length, TRUE, &match_result, &match_cnt);
1192                 jsdisp_release(regexp);
1193                 if(FAILED(hres)) {
1194                     SysFreeString(val_str);
1195                     return hres;
1196                 }
1197                 break;
1198             }
1199             jsdisp_release(regexp);
1200         }
1201     }
1202     default:
1203         hres = to_string(ctx, arg, ei, &match_str);
1204         if(FAILED(hres)) {
1205             SysFreeString(val_str);
1206             return hres;
1207         }
1208
1209         match_len = SysStringLen(match_str);
1210         if(!match_len) {
1211             SysFreeString(match_str);
1212             match_str = NULL;
1213         }
1214     }
1215
1216     hres = create_array(ctx, 0, &array);
1217
1218     if(SUCCEEDED(hres)) {
1219         ptr = str;
1220         for(i=0;; i++) {
1221             if(use_regexp) {
1222                 if(i == match_cnt)
1223                     break;
1224                 ptr2 = match_result[i].str;
1225             }else if(match_str) {
1226                 ptr2 = strstrW(ptr, match_str);
1227                 if(!ptr2)
1228                     break;
1229             }else {
1230                 if(!*ptr)
1231                     break;
1232                 ptr2 = ptr+1;
1233             }
1234
1235             V_VT(&var) = VT_BSTR;
1236             V_BSTR(&var) = SysAllocStringLen(ptr, ptr2-ptr);
1237             if(!V_BSTR(&var)) {
1238                 hres = E_OUTOFMEMORY;
1239                 break;
1240             }
1241
1242             hres = jsdisp_propput_idx(array, i, &var, ei);
1243             SysFreeString(V_BSTR(&var));
1244             if(FAILED(hres))
1245                 break;
1246
1247             if(use_regexp)
1248                 ptr = match_result[i].str + match_result[i].len;
1249             else if(match_str)
1250                 ptr = ptr2 + match_len;
1251             else
1252                 ptr++;
1253         }
1254     }
1255
1256     if(SUCCEEDED(hres) && (match_str || use_regexp)) {
1257         DWORD len = (str+length) - ptr;
1258
1259         if(len || match_str) {
1260             V_VT(&var) = VT_BSTR;
1261             V_BSTR(&var) = SysAllocStringLen(ptr, len);
1262
1263             if(V_BSTR(&var)) {
1264                 hres = jsdisp_propput_idx(array, i, &var, ei);
1265                 SysFreeString(V_BSTR(&var));
1266             }else {
1267                 hres = E_OUTOFMEMORY;
1268             }
1269         }
1270     }
1271
1272     SysFreeString(match_str);
1273     SysFreeString(val_str);
1274     heap_free(match_result);
1275
1276     if(SUCCEEDED(hres) && retv)
1277         var_set_jsdisp(retv, array);
1278     else
1279         jsdisp_release(array);
1280
1281     return hres;
1282 }
1283
1284 static HRESULT String_strike(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
1285         VARIANT *retv, jsexcept_t *ei)
1286 {
1287     static const WCHAR striketagW[] = {'S','T','R','I','K','E',0};
1288     return do_attributeless_tag_format(ctx, jsthis, flags, dp, retv, ei, striketagW);
1289 }
1290
1291 static HRESULT String_sub(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
1292         VARIANT *retv, jsexcept_t *ei)
1293 {
1294     static const WCHAR subtagW[] = {'S','U','B',0};
1295     return do_attributeless_tag_format(ctx, jsthis, flags, dp, retv, ei, subtagW);
1296 }
1297
1298 /* ECMA-262 3rd Edition    15.5.4.15 */
1299 static HRESULT String_substring(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
1300         VARIANT *retv, jsexcept_t *ei)
1301 {
1302     const WCHAR *str;
1303     BSTR val_str;
1304     INT start=0, end;
1305     DWORD length;
1306     VARIANT v;
1307     HRESULT hres;
1308
1309     TRACE("\n");
1310
1311     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
1312     if(FAILED(hres))
1313         return hres;
1314
1315     if(arg_cnt(dp) >= 1) {
1316         hres = to_integer(ctx, get_arg(dp,0), ei, &v);
1317         if(FAILED(hres)) {
1318             SysFreeString(val_str);
1319             return hres;
1320         }
1321
1322         if(V_VT(&v) == VT_I4) {
1323             start = V_I4(&v);
1324             if(start < 0)
1325                 start = 0;
1326             else if(start >= length)
1327                 start = length;
1328         }else {
1329             start = V_R8(&v) < 0.0 ? 0 : length;
1330         }
1331     }
1332
1333     if(arg_cnt(dp) >= 2) {
1334         hres = to_integer(ctx, get_arg(dp,1), ei, &v);
1335         if(FAILED(hres)) {
1336             SysFreeString(val_str);
1337             return hres;
1338         }
1339
1340         if(V_VT(&v) == VT_I4) {
1341             end = V_I4(&v);
1342             if(end < 0)
1343                 end = 0;
1344             else if(end > length)
1345                 end = length;
1346         }else {
1347             end = V_R8(&v) < 0.0 ? 0 : length;
1348         }
1349     }else {
1350         end = length;
1351     }
1352
1353     if(start > end) {
1354         INT tmp = start;
1355         start = end;
1356         end = tmp;
1357     }
1358
1359     if(retv) {
1360         V_VT(retv) = VT_BSTR;
1361         V_BSTR(retv) = SysAllocStringLen(str+start, end-start);
1362         if(!V_BSTR(retv)) {
1363             SysFreeString(val_str);
1364             return E_OUTOFMEMORY;
1365         }
1366     }
1367     SysFreeString(val_str);
1368     return S_OK;
1369 }
1370
1371 /* ECMA-262 3rd Edition    B.2.3 */
1372 static HRESULT String_substr(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
1373         VARIANT *retv, jsexcept_t *ei)
1374 {
1375     BSTR val_str;
1376     const WCHAR *str;
1377     INT start=0, len;
1378     DWORD length;
1379     VARIANT v;
1380     HRESULT hres;
1381
1382     TRACE("\n");
1383
1384     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
1385     if(FAILED(hres))
1386         return hres;
1387
1388     if(arg_cnt(dp) >= 1) {
1389         hres = to_integer(ctx, get_arg(dp,0), ei, &v);
1390         if(FAILED(hres)) {
1391             SysFreeString(val_str);
1392             return hres;
1393         }
1394
1395         if(V_VT(&v) == VT_I4) {
1396             start = V_I4(&v);
1397             if(start < 0)
1398                 start = 0;
1399             else if(start >= length)
1400                 start = length;
1401         }else {
1402             start = V_R8(&v) < 0.0 ? 0 : length;
1403         }
1404     }
1405
1406     if(arg_cnt(dp) >= 2) {
1407         hres = to_integer(ctx, get_arg(dp,1), ei, &v);
1408         if(FAILED(hres)) {
1409             SysFreeString(val_str);
1410             return hres;
1411         }
1412
1413         if(V_VT(&v) == VT_I4) {
1414             len = V_I4(&v);
1415             if(len < 0)
1416                 len = 0;
1417             else if(len > length-start)
1418                 len = length-start;
1419         }else {
1420             len = V_R8(&v) < 0.0 ? 0 : length-start;
1421         }
1422     }else {
1423         len = length-start;
1424     }
1425
1426     hres = S_OK;
1427     if(retv) {
1428         V_VT(retv) = VT_BSTR;
1429         V_BSTR(retv) = SysAllocStringLen(str+start, len);
1430         if(!V_BSTR(retv))
1431             hres = E_OUTOFMEMORY;
1432     }
1433
1434     SysFreeString(val_str);
1435     return hres;
1436 }
1437
1438 static HRESULT String_sup(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
1439         VARIANT *retv, jsexcept_t *ei)
1440 {
1441     static const WCHAR suptagW[] = {'S','U','P',0};
1442     return do_attributeless_tag_format(ctx, jsthis, flags, dp, retv, ei, suptagW);
1443 }
1444
1445 static HRESULT String_toLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
1446         VARIANT *retv, jsexcept_t *ei)
1447 {
1448     const WCHAR* str;
1449     DWORD length;
1450     BSTR val_str;
1451     HRESULT  hres;
1452
1453     TRACE("\n");
1454
1455     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
1456     if(FAILED(hres))
1457         return hres;
1458
1459     if(retv) {
1460         if(!val_str) {
1461             val_str = SysAllocStringLen(str, length);
1462             if(!val_str)
1463                 return E_OUTOFMEMORY;
1464         }
1465
1466         strlwrW(val_str);
1467
1468         V_VT(retv) = VT_BSTR;
1469         V_BSTR(retv) = val_str;
1470     }
1471     else SysFreeString(val_str);
1472     return S_OK;
1473 }
1474
1475 static HRESULT String_toUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
1476         VARIANT *retv, jsexcept_t *ei)
1477 {
1478     const WCHAR* str;
1479     DWORD length;
1480     BSTR val_str;
1481     HRESULT hres;
1482
1483     TRACE("\n");
1484
1485     hres = get_string_val(ctx, jsthis, ei, &str, &length, &val_str);
1486     if(FAILED(hres))
1487         return hres;
1488
1489     if(retv) {
1490         if(!val_str) {
1491             val_str = SysAllocStringLen(str, length);
1492             if(!val_str)
1493                 return E_OUTOFMEMORY;
1494         }
1495
1496         struprW(val_str);
1497
1498         V_VT(retv) = VT_BSTR;
1499         V_BSTR(retv) = val_str;
1500     }
1501     else SysFreeString(val_str);
1502     return S_OK;
1503 }
1504
1505 static HRESULT String_toLocaleLowerCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
1506         VARIANT *retv, jsexcept_t *ei)
1507 {
1508     FIXME("\n");
1509     return E_NOTIMPL;
1510 }
1511
1512 static HRESULT String_toLocaleUpperCase(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
1513         VARIANT *retv, jsexcept_t *ei)
1514 {
1515     FIXME("\n");
1516     return E_NOTIMPL;
1517 }
1518
1519 static HRESULT String_localeCompare(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
1520         VARIANT *retv, jsexcept_t *ei)
1521 {
1522     FIXME("\n");
1523     return E_NOTIMPL;
1524 }
1525
1526 static HRESULT String_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
1527         VARIANT *retv, jsexcept_t *ei)
1528 {
1529     StringInstance *This = string_from_vdisp(jsthis);
1530
1531     TRACE("\n");
1532
1533     switch(flags) {
1534     case INVOKE_FUNC:
1535         return throw_type_error(ctx, ei, JS_E_FUNCTION_EXPECTED, NULL);
1536     case DISPATCH_PROPERTYGET: {
1537         BSTR str = SysAllocString(This->str);
1538         if(!str)
1539             return E_OUTOFMEMORY;
1540
1541         V_VT(retv) = VT_BSTR;
1542         V_BSTR(retv) = str;
1543         break;
1544     }
1545     default:
1546         FIXME("flags %x\n", flags);
1547         return E_NOTIMPL;
1548     }
1549
1550     return S_OK;
1551 }
1552
1553 static void String_destructor(jsdisp_t *dispex)
1554 {
1555     StringInstance *This = (StringInstance*)dispex;
1556
1557     heap_free(This->str);
1558     heap_free(This);
1559 }
1560
1561 static const builtin_prop_t String_props[] = {
1562     {anchorW,                String_anchor,                PROPF_METHOD|1},
1563     {bigW,                   String_big,                   PROPF_METHOD},
1564     {blinkW,                 String_blink,                 PROPF_METHOD},
1565     {boldW,                  String_bold,                  PROPF_METHOD},
1566     {charAtW,                String_charAt,                PROPF_METHOD|1},
1567     {charCodeAtW,            String_charCodeAt,            PROPF_METHOD|1},
1568     {concatW,                String_concat,                PROPF_METHOD|1},
1569     {fixedW,                 String_fixed,                 PROPF_METHOD},
1570     {fontcolorW,             String_fontcolor,             PROPF_METHOD|1},
1571     {fontsizeW,              String_fontsize,              PROPF_METHOD|1},
1572     {indexOfW,               String_indexOf,               PROPF_METHOD|2},
1573     {italicsW,               String_italics,               PROPF_METHOD},
1574     {lastIndexOfW,           String_lastIndexOf,           PROPF_METHOD|2},
1575     {lengthW,                String_length,                0},
1576     {linkW,                  String_link,                  PROPF_METHOD|1},
1577     {localeCompareW,         String_localeCompare,         PROPF_METHOD|1},
1578     {matchW,                 String_match,                 PROPF_METHOD|1},
1579     {replaceW,               String_replace,               PROPF_METHOD|1},
1580     {searchW,                String_search,                PROPF_METHOD},
1581     {sliceW,                 String_slice,                 PROPF_METHOD},
1582     {smallW,                 String_small,                 PROPF_METHOD},
1583     {splitW,                 String_split,                 PROPF_METHOD|2},
1584     {strikeW,                String_strike,                PROPF_METHOD},
1585     {subW,                   String_sub,                   PROPF_METHOD},
1586     {substrW,                String_substr,                PROPF_METHOD|2},
1587     {substringW,             String_substring,             PROPF_METHOD|2},
1588     {supW,                   String_sup,                   PROPF_METHOD},
1589     {toLocaleLowerCaseW,     String_toLocaleLowerCase,     PROPF_METHOD},
1590     {toLocaleUpperCaseW,     String_toLocaleUpperCase,     PROPF_METHOD},
1591     {toLowerCaseW,           String_toLowerCase,           PROPF_METHOD},
1592     {toStringW,              String_toString,              PROPF_METHOD},
1593     {toUpperCaseW,           String_toUpperCase,           PROPF_METHOD},
1594     {valueOfW,               String_valueOf,               PROPF_METHOD}
1595 };
1596
1597 static const builtin_info_t String_info = {
1598     JSCLASS_STRING,
1599     {NULL, String_value, 0},
1600     sizeof(String_props)/sizeof(*String_props),
1601     String_props,
1602     String_destructor,
1603     NULL
1604 };
1605
1606 /* ECMA-262 3rd Edition    15.5.3.2 */
1607 static HRESULT StringConstr_fromCharCode(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
1608         DISPPARAMS *dp, VARIANT *retv, jsexcept_t *ei)
1609 {
1610     DWORD i, code;
1611     BSTR ret;
1612     HRESULT hres;
1613
1614     ret = SysAllocStringLen(NULL, arg_cnt(dp));
1615     if(!ret)
1616         return E_OUTOFMEMORY;
1617
1618     for(i=0; i<arg_cnt(dp); i++) {
1619         hres = to_uint32(ctx, get_arg(dp, i), ei, &code);
1620         if(FAILED(hres)) {
1621             SysFreeString(ret);
1622             return hres;
1623         }
1624
1625         ret[i] = code;
1626     }
1627
1628     if(retv) {
1629         V_VT(retv) = VT_BSTR;
1630         V_BSTR(retv) = ret;
1631     }
1632     else SysFreeString(ret);
1633
1634     return S_OK;
1635 }
1636
1637 static HRESULT StringConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, DISPPARAMS *dp,
1638         VARIANT *retv, jsexcept_t *ei)
1639 {
1640     HRESULT hres;
1641
1642     TRACE("\n");
1643
1644     switch(flags) {
1645     case INVOKE_FUNC: {
1646         BSTR str;
1647
1648         if(arg_cnt(dp)) {
1649             hres = to_string(ctx, get_arg(dp, 0), ei, &str);
1650             if(FAILED(hres))
1651                 return hres;
1652         }else {
1653             str = SysAllocStringLen(NULL, 0);
1654             if(!str)
1655                 return E_OUTOFMEMORY;
1656         }
1657
1658         V_VT(retv) = VT_BSTR;
1659         V_BSTR(retv) = str;
1660         break;
1661     }
1662     case DISPATCH_CONSTRUCT: {
1663         jsdisp_t *ret;
1664
1665         if(arg_cnt(dp)) {
1666             BSTR str;
1667
1668             hres = to_string(ctx, get_arg(dp, 0), ei, &str);
1669             if(FAILED(hres))
1670                 return hres;
1671
1672             hres = create_string(ctx, str, SysStringLen(str), &ret);
1673             SysFreeString(str);
1674         }else {
1675             hres = create_string(ctx, NULL, 0, &ret);
1676         }
1677
1678         if(FAILED(hres))
1679             return hres;
1680
1681         var_set_jsdisp(retv, ret);
1682         break;
1683     }
1684
1685     default:
1686         FIXME("unimplemented flags: %x\n", flags);
1687         return E_NOTIMPL;
1688     }
1689
1690     return S_OK;
1691 }
1692
1693 static HRESULT string_alloc(script_ctx_t *ctx, jsdisp_t *object_prototype, StringInstance **ret)
1694 {
1695     StringInstance *string;
1696     HRESULT hres;
1697
1698     string = heap_alloc_zero(sizeof(StringInstance));
1699     if(!string)
1700         return E_OUTOFMEMORY;
1701
1702     if(object_prototype)
1703         hres = init_dispex(&string->dispex, ctx, &String_info, object_prototype);
1704     else
1705         hres = init_dispex_from_constr(&string->dispex, ctx, &String_info, ctx->string_constr);
1706     if(FAILED(hres)) {
1707         heap_free(string);
1708         return hres;
1709     }
1710
1711     *ret = string;
1712     return S_OK;
1713 }
1714
1715 static const builtin_prop_t StringConstr_props[] = {
1716     {fromCharCodeW,    StringConstr_fromCharCode,    PROPF_METHOD},
1717 };
1718
1719 static const builtin_info_t StringConstr_info = {
1720     JSCLASS_FUNCTION,
1721     {NULL, Function_value, 0},
1722     sizeof(StringConstr_props)/sizeof(*StringConstr_props),
1723     StringConstr_props,
1724     NULL,
1725     NULL
1726 };
1727
1728 HRESULT create_string_constr(script_ctx_t *ctx, jsdisp_t *object_prototype, jsdisp_t **ret)
1729 {
1730     StringInstance *string;
1731     HRESULT hres;
1732
1733     static const WCHAR StringW[] = {'S','t','r','i','n','g',0};
1734
1735     hres = string_alloc(ctx, object_prototype, &string);
1736     if(FAILED(hres))
1737         return hres;
1738
1739     hres = create_builtin_function(ctx, StringConstr_value, StringW, &StringConstr_info,
1740             PROPF_CONSTR|1, &string->dispex, ret);
1741
1742     jsdisp_release(&string->dispex);
1743     return hres;
1744 }
1745
1746 HRESULT create_string(script_ctx_t *ctx, const WCHAR *str, DWORD len, jsdisp_t **ret)
1747 {
1748     StringInstance *string;
1749     HRESULT hres;
1750
1751     hres = string_alloc(ctx, NULL, &string);
1752     if(FAILED(hres))
1753         return hres;
1754
1755     if(len == -1)
1756         len = strlenW(str);
1757
1758     string->length = len;
1759     string->str = heap_alloc((len+1)*sizeof(WCHAR));
1760     if(!string->str) {
1761         jsdisp_release(&string->dispex);
1762         return E_OUTOFMEMORY;
1763     }
1764
1765     memcpy(string->str, str, len*sizeof(WCHAR));
1766     string->str[len] = 0;
1767
1768     *ret = &string->dispex;
1769     return S_OK;
1770
1771 }