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