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