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