xref: /reactos/dll/win32/msxml3/node.c (revision c2c66aff)
1 /*
2  *    Node implementation
3  *
4  * Copyright 2005 Mike McCormack
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "precomp.h"
22 
23 #ifdef HAVE_LIBXML2
24 # include <libxml/HTMLtree.h>
25 # ifdef SONAME_LIBXSLT
26 #  ifdef HAVE_LIBXSLT_PATTERN_H
27 #   include <libxslt/pattern.h>
28 #  endif
29 #  ifdef HAVE_LIBXSLT_TRANSFORM_H
30 #   include <libxslt/transform.h>
31 #  endif
32 #  include <libxslt/imports.h>
33 #  include <libxslt/variables.h>
34 #  include <libxslt/xsltutils.h>
35 #  include <libxslt/xsltInternals.h>
36 # endif
37 #endif
38 
39 #ifdef HAVE_LIBXML2
40 
41 #ifdef SONAME_LIBXSLT
42 extern void* libxslt_handle;
43 # define MAKE_FUNCPTR(f) extern typeof(f) * p##f
44 MAKE_FUNCPTR(xsltApplyStylesheet);
45 MAKE_FUNCPTR(xsltApplyStylesheetUser);
46 MAKE_FUNCPTR(xsltCleanupGlobals);
47 MAKE_FUNCPTR(xsltFreeStylesheet);
48 MAKE_FUNCPTR(xsltFreeTransformContext);
49 MAKE_FUNCPTR(xsltNewTransformContext);
50 MAKE_FUNCPTR(xsltNextImport);
51 MAKE_FUNCPTR(xsltParseStylesheetDoc);
52 MAKE_FUNCPTR(xsltQuoteUserParams);
53 MAKE_FUNCPTR(xsltSaveResultTo);
54 # undef MAKE_FUNCPTR
55 #else
56 WINE_DECLARE_DEBUG_CHANNEL(winediag);
57 #endif
58 
59 static const IID IID_xmlnode = {0x4f2f4ba2,0xb822,0x11df,{0x8b,0x8a,0x68,0x50,0xdf,0xd7,0x20,0x85}};
60 
61 xmlNodePtr xmlNodePtr_from_domnode( IXMLDOMNode *iface, xmlElementType type )
62 {
63     xmlnode *This;
64 
65     if ( !iface )
66         return NULL;
67     This = get_node_obj( iface );
68     if ( !This || !This->node )
69         return NULL;
70     if ( type && This->node->type != type )
71         return NULL;
72     return This->node;
73 }
74 
75 BOOL node_query_interface(xmlnode *This, REFIID riid, void **ppv)
76 {
77     if(IsEqualGUID(&IID_xmlnode, riid)) {
78         TRACE("(%p)->(IID_xmlnode %p)\n", This, ppv);
79         *ppv = This;
80         return TRUE;
81     }
82 
83     return dispex_query_interface(&This->dispex, riid, ppv);
84 }
85 
86 /* common ISupportErrorInfo implementation */
87 typedef struct {
88    ISupportErrorInfo ISupportErrorInfo_iface;
89    LONG ref;
90 
91    const tid_t* iids;
92 } SupportErrorInfo;
93 
94 static inline SupportErrorInfo *impl_from_ISupportErrorInfo(ISupportErrorInfo *iface)
95 {
96     return CONTAINING_RECORD(iface, SupportErrorInfo, ISupportErrorInfo_iface);
97 }
98 
99 static HRESULT WINAPI SupportErrorInfo_QueryInterface(ISupportErrorInfo *iface, REFIID riid, void **obj)
100 {
101     SupportErrorInfo *This = impl_from_ISupportErrorInfo(iface);
102     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), obj);
103 
104     *obj = NULL;
105 
106     if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISupportErrorInfo)) {
107         *obj = iface;
108         ISupportErrorInfo_AddRef(iface);
109         return S_OK;
110     }
111 
112     return E_NOINTERFACE;
113 }
114 
115 static ULONG WINAPI SupportErrorInfo_AddRef(ISupportErrorInfo *iface)
116 {
117     SupportErrorInfo *This = impl_from_ISupportErrorInfo(iface);
118     ULONG ref = InterlockedIncrement(&This->ref);
119     TRACE("(%p)->(%d)\n", This, ref );
120     return ref;
121 }
122 
123 static ULONG WINAPI SupportErrorInfo_Release(ISupportErrorInfo *iface)
124 {
125     SupportErrorInfo *This = impl_from_ISupportErrorInfo(iface);
126     LONG ref = InterlockedDecrement(&This->ref);
127 
128     TRACE("(%p)->(%d)\n", This, ref);
129 
130     if (ref == 0)
131         heap_free(This);
132 
133     return ref;
134 }
135 
136 static HRESULT WINAPI SupportErrorInfo_InterfaceSupportsErrorInfo(ISupportErrorInfo *iface, REFIID riid)
137 {
138     SupportErrorInfo *This = impl_from_ISupportErrorInfo(iface);
139     enum tid_t const *tid;
140 
141     TRACE("(%p)->(%s)\n", This, debugstr_guid(riid));
142 
143     tid = This->iids;
144     while (*tid != NULL_tid)
145     {
146         if (IsEqualGUID(riid, get_riid_from_tid(*tid)))
147             return S_OK;
148         tid++;
149     }
150 
151     return S_FALSE;
152 }
153 
154 static const struct ISupportErrorInfoVtbl SupportErrorInfoVtbl = {
155     SupportErrorInfo_QueryInterface,
156     SupportErrorInfo_AddRef,
157     SupportErrorInfo_Release,
158     SupportErrorInfo_InterfaceSupportsErrorInfo
159 };
160 
161 HRESULT node_create_supporterrorinfo(enum tid_t const *iids, void **obj)
162 {
163     SupportErrorInfo *This;
164 
165     This = heap_alloc(sizeof(*This));
166     if (!This) return E_OUTOFMEMORY;
167 
168     This->ISupportErrorInfo_iface.lpVtbl = &SupportErrorInfoVtbl;
169     This->ref = 1;
170     This->iids = iids;
171 
172     *obj = &This->ISupportErrorInfo_iface;
173 
174     return S_OK;
175 }
176 
177 xmlnode *get_node_obj(IXMLDOMNode *node)
178 {
179     xmlnode *obj = NULL;
180     HRESULT hres;
181 
182     hres = IXMLDOMNode_QueryInterface(node, &IID_xmlnode, (void**)&obj);
183     if (!obj) WARN("node is not our IXMLDOMNode implementation\n");
184     return SUCCEEDED(hres) ? obj : NULL;
185 }
186 
187 HRESULT node_get_nodeName(xmlnode *This, BSTR *name)
188 {
189     BSTR prefix, base;
190     HRESULT hr;
191 
192     if (!name)
193         return E_INVALIDARG;
194 
195     hr = node_get_base_name(This, &base);
196     if (hr != S_OK) return hr;
197 
198     hr = node_get_prefix(This, &prefix);
199     if (hr == S_OK)
200     {
201         static const WCHAR colW = ':';
202         WCHAR *ptr;
203 
204         /* +1 for ':' */
205         ptr = *name = SysAllocStringLen(NULL, SysStringLen(base) + SysStringLen(prefix) + 1);
206         memcpy(ptr, prefix, SysStringByteLen(prefix));
207         ptr += SysStringLen(prefix);
208         memcpy(ptr++, &colW, sizeof(WCHAR));
209         memcpy(ptr, base, SysStringByteLen(base));
210 
211         SysFreeString(base);
212         SysFreeString(prefix);
213     }
214     else
215         *name = base;
216 
217     return S_OK;
218 }
219 
220 HRESULT node_get_content(xmlnode *This, VARIANT *value)
221 {
222     xmlChar *content;
223 
224     if(!value)
225         return E_INVALIDARG;
226 
227     content = xmlNodeGetContent(This->node);
228     V_VT(value) = VT_BSTR;
229     V_BSTR(value) = bstr_from_xmlChar( content );
230     xmlFree(content);
231 
232     TRACE("%p returned %s\n", This, debugstr_w(V_BSTR(value)));
233     return S_OK;
234 }
235 
236 HRESULT node_set_content(xmlnode *This, LPCWSTR value)
237 {
238     xmlChar *str;
239 
240     TRACE("(%p)->(%s)\n", This, debugstr_w(value));
241     str = xmlchar_from_wchar(value);
242     if(!str)
243         return E_OUTOFMEMORY;
244 
245     xmlNodeSetContent(This->node, str);
246     heap_free(str);
247     return S_OK;
248 }
249 
250 static HRESULT node_set_content_escaped(xmlnode *This, LPCWSTR value)
251 {
252     xmlChar *str, *escaped;
253 
254     TRACE("(%p)->(%s)\n", This, debugstr_w(value));
255     str = xmlchar_from_wchar(value);
256     if(!str)
257         return E_OUTOFMEMORY;
258 
259     escaped = xmlEncodeSpecialChars(NULL, str);
260     if(!escaped)
261     {
262         heap_free(str);
263         return E_OUTOFMEMORY;
264     }
265 
266     xmlNodeSetContent(This->node, escaped);
267 
268     heap_free(str);
269     xmlFree(escaped);
270 
271     return S_OK;
272 }
273 
274 HRESULT node_put_value(xmlnode *This, VARIANT *value)
275 {
276     HRESULT hr;
277 
278     if (V_VT(value) != VT_BSTR)
279     {
280         VARIANT string_value;
281 
282         VariantInit(&string_value);
283         hr = VariantChangeType(&string_value, value, 0, VT_BSTR);
284         if(FAILED(hr)) {
285             WARN("Couldn't convert to VT_BSTR\n");
286             return hr;
287         }
288 
289         hr = node_set_content(This, V_BSTR(&string_value));
290         VariantClear(&string_value);
291     }
292     else
293         hr = node_set_content(This, V_BSTR(value));
294 
295     return hr;
296 }
297 
298 HRESULT node_put_value_escaped(xmlnode *This, VARIANT *value)
299 {
300     HRESULT hr;
301 
302     if (V_VT(value) != VT_BSTR)
303     {
304        VARIANT string_value;
305 
306         VariantInit(&string_value);
307         hr = VariantChangeType(&string_value, value, 0, VT_BSTR);
308         if(FAILED(hr)) {
309             WARN("Couldn't convert to VT_BSTR\n");
310             return hr;
311         }
312 
313         hr = node_set_content_escaped(This, V_BSTR(&string_value));
314         VariantClear(&string_value);
315     }
316     else
317         hr = node_set_content_escaped(This, V_BSTR(value));
318 
319     return hr;
320 }
321 
322 static HRESULT get_node(
323     xmlnode *This,
324     const char *name,
325     xmlNodePtr node,
326     IXMLDOMNode **out )
327 {
328     TRACE("(%p)->(%s %p %p)\n", This, name, node, out );
329 
330     if ( !out )
331         return E_INVALIDARG;
332 
333     /* if we don't have a doc, use our parent. */
334     if(node && !node->doc && node->parent)
335         node->doc = node->parent->doc;
336 
337     *out = create_node( node );
338     if (!*out)
339         return S_FALSE;
340     return S_OK;
341 }
342 
343 HRESULT node_get_parent(xmlnode *This, IXMLDOMNode **parent)
344 {
345     return get_node( This, "parent", This->node->parent, parent );
346 }
347 
348 HRESULT node_get_child_nodes(xmlnode *This, IXMLDOMNodeList **ret)
349 {
350     if(!ret)
351         return E_INVALIDARG;
352 
353     *ret = create_children_nodelist(This->node);
354     if(!*ret)
355         return E_OUTOFMEMORY;
356 
357     return S_OK;
358 }
359 
360 HRESULT node_get_first_child(xmlnode *This, IXMLDOMNode **ret)
361 {
362     return get_node(This, "firstChild", This->node->children, ret);
363 }
364 
365 HRESULT node_get_last_child(xmlnode *This, IXMLDOMNode **ret)
366 {
367     return get_node(This, "lastChild", This->node->last, ret);
368 }
369 
370 HRESULT node_get_previous_sibling(xmlnode *This, IXMLDOMNode **ret)
371 {
372     return get_node(This, "previous", This->node->prev, ret);
373 }
374 
375 HRESULT node_get_next_sibling(xmlnode *This, IXMLDOMNode **ret)
376 {
377     return get_node(This, "next", This->node->next, ret);
378 }
379 
380 static int node_get_inst_cnt(xmlNodePtr node)
381 {
382     int ret = *(LONG *)&node->_private & NODE_PRIV_REFCOUNT_MASK;
383     xmlNodePtr child;
384 
385     /* add attribute counts */
386     if (node->type == XML_ELEMENT_NODE)
387     {
388         xmlAttrPtr prop = node->properties;
389 
390         while (prop)
391         {
392             ret += node_get_inst_cnt((xmlNodePtr)prop);
393             prop = prop->next;
394         }
395     }
396 
397     /* add children counts */
398     child = node->children;
399     while (child)
400     {
401         ret += node_get_inst_cnt(child);
402         child = child->next;
403     }
404 
405     return ret;
406 }
407 
408 int xmlnode_get_inst_cnt(xmlnode *node)
409 {
410     return node_get_inst_cnt(node->node);
411 }
412 
413 /* _private field holds a number of COM instances spawned from this libxml2 node
414  * most significant bits are used to store information about ignorrable whitespace nodes */
415 void xmlnode_add_ref(xmlNodePtr node)
416 {
417     if (node->type == XML_DOCUMENT_NODE) return;
418     InterlockedIncrement((LONG*)&node->_private);
419 }
420 
421 void xmlnode_release(xmlNodePtr node)
422 {
423     if (node->type == XML_DOCUMENT_NODE) return;
424     InterlockedDecrement((LONG*)&node->_private);
425 }
426 
427 HRESULT node_insert_before(xmlnode *This, IXMLDOMNode *new_child, const VARIANT *ref_child,
428         IXMLDOMNode **ret)
429 {
430     IXMLDOMNode *before = NULL;
431     xmlnode *node_obj;
432     int refcount = 0;
433     xmlDocPtr doc;
434     HRESULT hr;
435 
436     if(!new_child)
437         return E_INVALIDARG;
438 
439     node_obj = get_node_obj(new_child);
440     if(!node_obj) return E_FAIL;
441 
442     switch(V_VT(ref_child))
443     {
444     case VT_EMPTY:
445     case VT_NULL:
446         break;
447 
448     case VT_UNKNOWN:
449     case VT_DISPATCH:
450         if (V_UNKNOWN(ref_child))
451         {
452             hr = IUnknown_QueryInterface(V_UNKNOWN(ref_child), &IID_IXMLDOMNode, (void**)&before);
453             if(FAILED(hr)) return hr;
454         }
455         break;
456 
457     default:
458         FIXME("refChild var type %x\n", V_VT(ref_child));
459         return E_FAIL;
460     }
461 
462     TRACE("new child %p, This->node %p\n", node_obj->node, This->node);
463 
464     if(!node_obj->node->parent)
465         if(xmldoc_remove_orphan(node_obj->node->doc, node_obj->node) != S_OK)
466             WARN("%p is not an orphan of %p\n", node_obj->node, node_obj->node->doc);
467 
468     refcount = xmlnode_get_inst_cnt(node_obj);
469 
470     if(before)
471     {
472         xmlnode *before_node_obj = get_node_obj(before);
473         IXMLDOMNode_Release(before);
474         if(!before_node_obj) return E_FAIL;
475     }
476 
477     /* unlink from current parent first */
478     if(node_obj->parent)
479     {
480         hr = IXMLDOMNode_removeChild(node_obj->parent, node_obj->iface, NULL);
481         if (hr == S_OK) xmldoc_remove_orphan(node_obj->node->doc, node_obj->node);
482     }
483     doc = node_obj->node->doc;
484 
485     if(before)
486     {
487         xmlNodePtr new_node;
488         xmlnode *before_node_obj = get_node_obj(before);
489 
490         /* refs count including subtree */
491         if (doc != before_node_obj->node->doc)
492             refcount = xmlnode_get_inst_cnt(node_obj);
493 
494         if (refcount) xmldoc_add_refs(before_node_obj->node->doc, refcount);
495         new_node = xmlAddPrevSibling(before_node_obj->node, node_obj->node);
496         if (new_node != node_obj->node)
497         {
498             if (refcount != 1)
499                 FIXME("referenced xmlNode was freed, expect crashes\n");
500             xmlnode_add_ref(new_node);
501             node_obj->node = new_node;
502         }
503         if (refcount) xmldoc_release_refs(doc, refcount);
504         node_obj->parent = This->parent;
505     }
506     else
507     {
508         xmlNodePtr new_node;
509 
510         if (doc != This->node->doc)
511             refcount = xmlnode_get_inst_cnt(node_obj);
512 
513         if (refcount) xmldoc_add_refs(This->node->doc, refcount);
514         /* xmlAddChild doesn't unlink node from previous parent */
515         xmlUnlinkNode(node_obj->node);
516         new_node = xmlAddChild(This->node, node_obj->node);
517         if (new_node != node_obj->node)
518         {
519             if (refcount != 1)
520                 FIXME("referenced xmlNode was freed, expect crashes\n");
521             xmlnode_add_ref(new_node);
522             node_obj->node = new_node;
523         }
524         if (refcount) xmldoc_release_refs(doc, refcount);
525         node_obj->parent = This->iface;
526     }
527 
528     if(ret)
529     {
530         IXMLDOMNode_AddRef(new_child);
531         *ret = new_child;
532     }
533 
534     TRACE("ret S_OK\n");
535     return S_OK;
536 }
537 
538 HRESULT node_replace_child(xmlnode *This, IXMLDOMNode *newChild, IXMLDOMNode *oldChild,
539         IXMLDOMNode **ret)
540 {
541     xmlnode *old_child, *new_child;
542     xmlDocPtr leaving_doc;
543     xmlNode *my_ancestor;
544     int refcount = 0;
545 
546     /* Do not believe any documentation telling that newChild == NULL
547        means removal. It does certainly *not* apply to msxml3! */
548     if(!newChild || !oldChild)
549         return E_INVALIDARG;
550 
551     if(ret)
552         *ret = NULL;
553 
554     old_child = get_node_obj(oldChild);
555     if(!old_child) return E_FAIL;
556 
557     if(old_child->node->parent != This->node)
558     {
559         WARN("childNode %p is not a child of %p\n", oldChild, This);
560         return E_INVALIDARG;
561     }
562 
563     new_child = get_node_obj(newChild);
564     if(!new_child) return E_FAIL;
565 
566     my_ancestor = This->node;
567     while(my_ancestor)
568     {
569         if(my_ancestor == new_child->node)
570         {
571             WARN("tried to create loop\n");
572             return E_FAIL;
573         }
574         my_ancestor = my_ancestor->parent;
575     }
576 
577     if(!new_child->node->parent)
578         if(xmldoc_remove_orphan(new_child->node->doc, new_child->node) != S_OK)
579             WARN("%p is not an orphan of %p\n", new_child->node, new_child->node->doc);
580 
581     leaving_doc = new_child->node->doc;
582 
583     if (leaving_doc != old_child->node->doc)
584         refcount = xmlnode_get_inst_cnt(new_child);
585 
586     if (refcount) xmldoc_add_refs(old_child->node->doc, refcount);
587     xmlReplaceNode(old_child->node, new_child->node);
588     if (refcount) xmldoc_release_refs(leaving_doc, refcount);
589     new_child->parent = old_child->parent;
590     old_child->parent = NULL;
591 
592     xmldoc_add_orphan(old_child->node->doc, old_child->node);
593 
594     if(ret)
595     {
596         IXMLDOMNode_AddRef(oldChild);
597         *ret = oldChild;
598     }
599 
600     return S_OK;
601 }
602 
603 HRESULT node_remove_child(xmlnode *This, IXMLDOMNode* child, IXMLDOMNode** oldChild)
604 {
605     xmlnode *child_node;
606 
607     if(!child) return E_INVALIDARG;
608 
609     if(oldChild)
610         *oldChild = NULL;
611 
612     child_node = get_node_obj(child);
613     if(!child_node) return E_FAIL;
614 
615     if(child_node->node->parent != This->node)
616     {
617         WARN("childNode %p is not a child of %p\n", child, This);
618         return E_INVALIDARG;
619     }
620 
621     xmlUnlinkNode(child_node->node);
622     child_node->parent = NULL;
623     xmldoc_add_orphan(child_node->node->doc, child_node->node);
624 
625     if(oldChild)
626     {
627         IXMLDOMNode_AddRef(child);
628         *oldChild = child;
629     }
630 
631     return S_OK;
632 }
633 
634 HRESULT node_append_child(xmlnode *This, IXMLDOMNode *child, IXMLDOMNode **outChild)
635 {
636     DOMNodeType type;
637     VARIANT var;
638     HRESULT hr;
639 
640     if (!child)
641         return E_INVALIDARG;
642 
643     hr = IXMLDOMNode_get_nodeType(child, &type);
644     if(FAILED(hr) || type == NODE_ATTRIBUTE) {
645         if (outChild) *outChild = NULL;
646         return E_FAIL;
647     }
648 
649     VariantInit(&var);
650     return IXMLDOMNode_insertBefore(This->iface, child, var, outChild);
651 }
652 
653 HRESULT node_has_childnodes(const xmlnode *This, VARIANT_BOOL *ret)
654 {
655     if (!ret) return E_INVALIDARG;
656 
657     if (!This->node->children)
658     {
659         *ret = VARIANT_FALSE;
660         return S_FALSE;
661     }
662 
663     *ret = VARIANT_TRUE;
664     return S_OK;
665 }
666 
667 HRESULT node_get_owner_doc(const xmlnode *This, IXMLDOMDocument **doc)
668 {
669     if(!doc)
670         return E_INVALIDARG;
671     return get_domdoc_from_xmldoc(This->node->doc, (IXMLDOMDocument3**)doc);
672 }
673 
674 HRESULT node_clone(xmlnode *This, VARIANT_BOOL deep, IXMLDOMNode **cloneNode)
675 {
676     IXMLDOMNode *node;
677     xmlNodePtr clone;
678 
679     if(!cloneNode) return E_INVALIDARG;
680 
681     clone = xmlCopyNode(This->node, deep ? 1 : 2);
682     if (clone)
683     {
684         xmlSetTreeDoc(clone, This->node->doc);
685         xmldoc_add_orphan(clone->doc, clone);
686 
687         node = create_node(clone);
688         if (!node)
689         {
690             ERR("Copy failed\n");
691             xmldoc_remove_orphan(clone->doc, clone);
692             xmlFreeNode(clone);
693             return E_FAIL;
694         }
695 
696         *cloneNode = node;
697     }
698     else
699     {
700         ERR("Copy failed\n");
701         return E_FAIL;
702     }
703 
704     return S_OK;
705 }
706 
707 static xmlChar* do_get_text(xmlNodePtr node, BOOL trim, DWORD *first, DWORD *last, BOOL *trail_ig_ws)
708 {
709     xmlNodePtr child;
710     xmlChar* str;
711     BOOL preserving = is_preserving_whitespace(node);
712 
713     *first = -1;
714     *last = 0;
715 
716     if (!node->children)
717     {
718         str = xmlNodeGetContent(node);
719         *trail_ig_ws = *(DWORD*)&node->_private & NODE_PRIV_CHILD_IGNORABLE_WS;
720     }
721     else
722     {
723         BOOL ig_ws = FALSE;
724         xmlChar* tmp;
725         DWORD pos = 0;
726         str = xmlStrdup(BAD_CAST "");
727 
728         if (node->type != XML_DOCUMENT_NODE)
729             ig_ws = *(DWORD*)&node->_private & NODE_PRIV_CHILD_IGNORABLE_WS;
730         *trail_ig_ws = FALSE;
731 
732         for (child = node->children; child != NULL; child = child->next)
733         {
734             switch (child->type)
735             {
736             case XML_ELEMENT_NODE: {
737                 DWORD node_first, node_last;
738 
739                 tmp = do_get_text(child, FALSE, &node_first, &node_last, trail_ig_ws);
740 
741                 if (node_first!=-1 && pos+node_first<*first)
742                     *first = pos+node_first;
743                 if (node_last && pos+node_last>*last)
744                     *last = pos+node_last;
745                 break;
746             }
747             case XML_TEXT_NODE:
748                 tmp = xmlNodeGetContent(child);
749                 if (!preserving && tmp[0])
750                 {
751                     xmlChar *beg;
752 
753                     for (beg = tmp; *beg; beg++)
754                         if (!isspace(*beg)) break;
755 
756                     if (!*beg)
757                     {
758                         ig_ws = TRUE;
759                         xmlFree(tmp);
760                         tmp = NULL;
761                     }
762                 }
763                 break;
764             case XML_CDATA_SECTION_NODE:
765             case XML_ENTITY_REF_NODE:
766             case XML_ENTITY_NODE:
767                 tmp = xmlNodeGetContent(child);
768                 break;
769             default:
770                 tmp = NULL;
771                 break;
772             }
773 
774             if ((tmp && *tmp) || child->type==XML_CDATA_SECTION_NODE)
775             {
776                 if (ig_ws && str[0])
777                 {
778                     str = xmlStrcat(str, BAD_CAST " ");
779                     pos++;
780                 }
781                 if (tmp && *tmp) str = xmlStrcat(str, tmp);
782                 if (child->type==XML_CDATA_SECTION_NODE && pos<*first)
783                     *first = pos;
784                 if (tmp && *tmp) pos += xmlStrlen(tmp);
785                 if (child->type==XML_CDATA_SECTION_NODE && pos>*last)
786                     *last = pos;
787                 ig_ws = FALSE;
788             }
789             if (tmp) xmlFree(tmp);
790 
791             if (!ig_ws)
792             {
793                 ig_ws = *(DWORD*)&child->_private & NODE_PRIV_TRAILING_IGNORABLE_WS;
794             }
795             if (!ig_ws)
796                 ig_ws = *trail_ig_ws;
797             *trail_ig_ws = FALSE;
798         }
799 
800         *trail_ig_ws = ig_ws;
801     }
802 
803     switch (node->type)
804     {
805     case XML_ELEMENT_NODE:
806     case XML_TEXT_NODE:
807     case XML_ENTITY_REF_NODE:
808     case XML_ENTITY_NODE:
809     case XML_DOCUMENT_NODE:
810     case XML_DOCUMENT_FRAG_NODE:
811         if (trim && !preserving)
812         {
813             xmlChar* ret;
814             int len;
815 
816             if (!str)
817                 break;
818 
819             for (ret = str; *ret && isspace(*ret) && (*first)--; ret++)
820                 if (*last) (*last)--;
821             for (len = xmlStrlen(ret)-1; len >= 0 && len >= *last; len--)
822                 if(!isspace(ret[len])) break;
823 
824             ret = xmlStrndup(ret, len+1);
825             xmlFree(str);
826             str = ret;
827             break;
828         }
829         break;
830     default:
831         break;
832     }
833 
834     return str;
835 }
836 
837 HRESULT node_get_text(const xmlnode *This, BSTR *text)
838 {
839     BSTR str = NULL;
840     xmlChar *content;
841     DWORD first, last;
842     BOOL tmp;
843 
844     if (!text) return E_INVALIDARG;
845 
846     content = do_get_text(This->node, TRUE, &first, &last, &tmp);
847     if (content)
848     {
849         str = bstr_from_xmlChar(content);
850         xmlFree(content);
851     }
852 
853     /* Always return a string. */
854     if (!str) str = SysAllocStringLen( NULL, 0 );
855 
856     TRACE("%p %s\n", This, debugstr_w(str) );
857     *text = str;
858 
859     return S_OK;
860 }
861 
862 HRESULT node_put_text(xmlnode *This, BSTR text)
863 {
864     xmlChar *str, *str2;
865 
866     TRACE("(%p)->(%s)\n", This, debugstr_w(text));
867 
868     str = xmlchar_from_wchar(text);
869 
870     /* Escape the string. */
871     str2 = xmlEncodeEntitiesReentrant(This->node->doc, str);
872     heap_free(str);
873 
874     xmlNodeSetContent(This->node, str2);
875     xmlFree(str2);
876 
877     return S_OK;
878 }
879 
880 BSTR EnsureCorrectEOL(BSTR sInput)
881 {
882     int nNum = 0;
883     BSTR sNew;
884     int nLen;
885     int i;
886 
887     nLen = SysStringLen(sInput);
888     /* Count line endings */
889     for(i=0; i < nLen; i++)
890     {
891         if(sInput[i] == '\n')
892             nNum++;
893     }
894 
895     TRACE("len=%d, num=%d\n", nLen, nNum);
896 
897     /* Add linefeed as needed */
898     if(nNum > 0)
899     {
900         int nPlace = 0;
901         sNew = SysAllocStringLen(NULL, nLen + nNum);
902         for(i=0; i < nLen; i++)
903         {
904             if(sInput[i] == '\n')
905             {
906                 sNew[i+nPlace] = '\r';
907                 nPlace++;
908             }
909             sNew[i+nPlace] = sInput[i];
910         }
911 
912         SysFreeString(sInput);
913     }
914     else
915     {
916         sNew = sInput;
917     }
918 
919     TRACE("len %d\n", SysStringLen(sNew));
920 
921     return sNew;
922 }
923 
924 /*
925  * We are trying to replicate the same behaviour as msxml by converting
926  * line endings to \r\n and using indents as \t. The problem is that msxml
927  * only formats nodes that have a line ending. Using libxml we cannot
928  * reproduce behaviour exactly.
929  *
930  */
931 HRESULT node_get_xml(xmlnode *This, BOOL ensure_eol, BSTR *ret)
932 {
933     xmlBufferPtr xml_buf;
934     xmlNodePtr xmldecl;
935     int size;
936 
937     if(!ret)
938         return E_INVALIDARG;
939 
940     *ret = NULL;
941 
942     xml_buf = xmlBufferCreate();
943     if(!xml_buf)
944         return E_OUTOFMEMORY;
945 
946     xmldecl = xmldoc_unlink_xmldecl( This->node->doc );
947 
948     size = xmlNodeDump(xml_buf, This->node->doc, This->node, 0, 1);
949     if(size > 0) {
950         const xmlChar *buf_content;
951         BSTR content;
952 
953         /* Attribute Nodes return a space in front of their name */
954         buf_content = xmlBufferContent(xml_buf);
955 
956         content = bstr_from_xmlChar(buf_content + (buf_content[0] == ' ' ? 1 : 0));
957         if(ensure_eol)
958             content = EnsureCorrectEOL(content);
959 
960         *ret = content;
961     }else {
962         *ret = SysAllocStringLen(NULL, 0);
963     }
964 
965     xmlBufferFree(xml_buf);
966     xmldoc_link_xmldecl( This->node->doc, xmldecl );
967     return *ret ? S_OK : E_OUTOFMEMORY;
968 }
969 
970 #ifdef SONAME_LIBXSLT
971 
972 /* duplicates xmlBufferWriteQuotedString() logic */
973 static void xml_write_quotedstring(xmlOutputBufferPtr buf, const xmlChar *string)
974 {
975     const xmlChar *cur, *base;
976 
977     if (xmlStrchr(string, '\"'))
978     {
979         if (xmlStrchr(string, '\''))
980         {
981             xmlOutputBufferWrite(buf, 1, "\"");
982             base = cur = string;
983 
984             while (*cur)
985             {
986                 if (*cur == '"')
987                 {
988                     if (base != cur)
989                         xmlOutputBufferWrite(buf, cur-base, (const char*)base);
990                     xmlOutputBufferWrite(buf, 6, "&quot;");
991                     cur++;
992                     base = cur;
993                 }
994                 else
995                     cur++;
996             }
997             if (base != cur)
998                 xmlOutputBufferWrite(buf, cur-base, (const char*)base);
999             xmlOutputBufferWrite(buf, 1, "\"");
1000         }
1001         else
1002         {
1003             xmlOutputBufferWrite(buf, 1, "\'");
1004             xmlOutputBufferWriteString(buf, (const char*)string);
1005             xmlOutputBufferWrite(buf, 1, "\'");
1006         }
1007     }
1008     else
1009     {
1010         xmlOutputBufferWrite(buf, 1, "\"");
1011         xmlOutputBufferWriteString(buf, (const char*)string);
1012         xmlOutputBufferWrite(buf, 1, "\"");
1013     }
1014 }
1015 
1016 static int XMLCALL transform_to_stream_write(void *context, const char *buffer, int len)
1017 {
1018     DWORD written;
1019     HRESULT hr = ISequentialStream_Write((ISequentialStream *)context, buffer, len, &written);
1020     return hr == S_OK ? written : -1;
1021 }
1022 
1023 /* Output for method "text" */
1024 static void transform_write_text(xmlDocPtr result, xsltStylesheetPtr style, xmlOutputBufferPtr output)
1025 {
1026     xmlNodePtr cur = result->children;
1027     while (cur)
1028     {
1029         if (cur->type == XML_TEXT_NODE)
1030             xmlOutputBufferWriteString(output, (const char*)cur->content);
1031 
1032         /* skip to next node */
1033         if (cur->children)
1034         {
1035             if ((cur->children->type != XML_ENTITY_DECL) &&
1036                 (cur->children->type != XML_ENTITY_REF_NODE) &&
1037                 (cur->children->type != XML_ENTITY_NODE))
1038             {
1039                 cur = cur->children;
1040                 continue;
1041             }
1042         }
1043 
1044         if (cur->next) {
1045             cur = cur->next;
1046             continue;
1047         }
1048 
1049         do
1050         {
1051             cur = cur->parent;
1052             if (cur == NULL)
1053                 break;
1054             if (cur == (xmlNodePtr) style->doc) {
1055                 cur = NULL;
1056                 break;
1057             }
1058             if (cur->next) {
1059                 cur = cur->next;
1060                 break;
1061             }
1062         } while (cur);
1063     }
1064 }
1065 
1066 #undef XSLT_GET_IMPORT_PTR
1067 #define XSLT_GET_IMPORT_PTR(res, style, name) {          \
1068     xsltStylesheetPtr st = style;                        \
1069     res = NULL;                                          \
1070     while (st != NULL) {                                 \
1071         if (st->name != NULL) { res = st->name; break; } \
1072         st = pxsltNextImport(st);                        \
1073     }}
1074 
1075 #undef XSLT_GET_IMPORT_INT
1076 #define XSLT_GET_IMPORT_INT(res, style, name) {         \
1077     xsltStylesheetPtr st = style;                       \
1078     res = -1;                                           \
1079     while (st != NULL) {                                \
1080         if (st->name != -1) { res = st->name; break; }  \
1081         st = pxsltNextImport(st);                       \
1082     }}
1083 
1084 static void transform_write_xmldecl(xmlDocPtr result, xsltStylesheetPtr style, BOOL omit_encoding, xmlOutputBufferPtr output)
1085 {
1086     int omit_xmldecl, standalone;
1087 
1088     XSLT_GET_IMPORT_INT(omit_xmldecl, style, omitXmlDeclaration);
1089     if (omit_xmldecl == 1) return;
1090 
1091     XSLT_GET_IMPORT_INT(standalone, style, standalone);
1092 
1093     xmlOutputBufferWriteString(output, "<?xml version=");
1094     if (result->version)
1095     {
1096         xmlOutputBufferWriteString(output, "\"");
1097         xmlOutputBufferWriteString(output, (const char *)result->version);
1098         xmlOutputBufferWriteString(output, "\"");
1099     }
1100     else
1101         xmlOutputBufferWriteString(output, "\"1.0\"");
1102 
1103     if (!omit_encoding)
1104     {
1105         const xmlChar *encoding;
1106 
1107         /* default encoding is UTF-16 */
1108         XSLT_GET_IMPORT_PTR(encoding, style, encoding);
1109         xmlOutputBufferWriteString(output, " encoding=");
1110         xmlOutputBufferWriteString(output, "\"");
1111         xmlOutputBufferWriteString(output, encoding ? (const char *)encoding : "UTF-16");
1112         xmlOutputBufferWriteString(output, "\"");
1113     }
1114 
1115     /* standalone attribute */
1116     if (standalone != -1)
1117         xmlOutputBufferWriteString(output, standalone == 0 ? " standalone=\"no\"" : " standalone=\"yes\"");
1118 
1119     xmlOutputBufferWriteString(output, "?>");
1120 }
1121 
1122 static void htmldtd_dumpcontent(xmlOutputBufferPtr buf, xmlDocPtr doc)
1123 {
1124     xmlDtdPtr cur = doc->intSubset;
1125 
1126     xmlOutputBufferWriteString(buf, "<!DOCTYPE ");
1127     xmlOutputBufferWriteString(buf, (const char *)cur->name);
1128     if (cur->ExternalID)
1129     {
1130         xmlOutputBufferWriteString(buf, " PUBLIC ");
1131         xml_write_quotedstring(buf, cur->ExternalID);
1132         if (cur->SystemID)
1133         {
1134             xmlOutputBufferWriteString(buf, " ");
1135             xml_write_quotedstring(buf, cur->SystemID);
1136         }
1137     }
1138     else if (cur->SystemID)
1139     {
1140         xmlOutputBufferWriteString(buf, " SYSTEM ");
1141         xml_write_quotedstring(buf, cur->SystemID);
1142     }
1143     xmlOutputBufferWriteString(buf, ">\n");
1144 }
1145 
1146 /* Duplicates htmlDocContentDumpFormatOutput() the way we need it - doesn't add trailing newline. */
1147 static void htmldoc_dumpcontent(xmlOutputBufferPtr buf, xmlDocPtr doc, const char *encoding, int format)
1148 {
1149     xmlElementType type;
1150 
1151     /* force HTML output */
1152     type = doc->type;
1153     doc->type = XML_HTML_DOCUMENT_NODE;
1154     if (doc->intSubset)
1155         htmldtd_dumpcontent(buf, doc);
1156     if (doc->children) {
1157         xmlNodePtr cur = doc->children;
1158         while (cur) {
1159             htmlNodeDumpFormatOutput(buf, doc, cur, encoding, format);
1160             cur = cur->next;
1161         }
1162     }
1163     doc->type = type;
1164 }
1165 
1166 static inline BOOL transform_is_empty_resultdoc(xmlDocPtr result)
1167 {
1168     return !result->children || ((result->children->type == XML_DTD_NODE) && !result->children->next);
1169 }
1170 
1171 static inline BOOL transform_is_valid_method(xsltStylesheetPtr style)
1172 {
1173     return !style->methodURI || !(style->method && xmlStrEqual(style->method, (const xmlChar *)"xhtml"));
1174 }
1175 
1176 /* Helper to write transformation result to specified output buffer. */
1177 static HRESULT node_transform_write(xsltStylesheetPtr style, xmlDocPtr result, BOOL omit_encoding, const char *encoding, xmlOutputBufferPtr output)
1178 {
1179     const xmlChar *method;
1180     int indent;
1181 
1182     if (!transform_is_valid_method(style))
1183     {
1184         ERR("unknown output method\n");
1185         return E_FAIL;
1186     }
1187 
1188     XSLT_GET_IMPORT_PTR(method, style, method)
1189     XSLT_GET_IMPORT_INT(indent, style, indent);
1190 
1191     if (!method && (result->type == XML_HTML_DOCUMENT_NODE))
1192         method = (const xmlChar *) "html";
1193 
1194     if (method && xmlStrEqual(method, (const xmlChar *)"html"))
1195     {
1196         htmlSetMetaEncoding(result, (const xmlChar *)encoding);
1197         if (indent == -1)
1198             indent = 1;
1199         htmldoc_dumpcontent(output, result, (const char*)encoding, indent);
1200     }
1201     else if (method && xmlStrEqual(method, (const xmlChar *)"xhtml"))
1202     {
1203         htmlSetMetaEncoding(result, (const xmlChar *) encoding);
1204         htmlDocContentDumpOutput(output, result, encoding);
1205     }
1206     else if (method && xmlStrEqual(method, (const xmlChar *)"text"))
1207         transform_write_text(result, style, output);
1208     else
1209     {
1210         transform_write_xmldecl(result, style, omit_encoding, output);
1211 
1212         if (result->children)
1213         {
1214             xmlNodePtr child = result->children;
1215 
1216             while (child)
1217             {
1218                 xmlNodeDumpOutput(output, result, child, 0, indent == 1, encoding);
1219                 if (indent && ((child->type == XML_DTD_NODE) || ((child->type == XML_COMMENT_NODE) && child->next)))
1220                     xmlOutputBufferWriteString(output, "\r\n");
1221                 child = child->next;
1222             }
1223         }
1224     }
1225 
1226     xmlOutputBufferFlush(output);
1227     return S_OK;
1228 }
1229 
1230 /* For BSTR output is always UTF-16, without 'encoding' attribute */
1231 static HRESULT node_transform_write_to_bstr(xsltStylesheetPtr style, xmlDocPtr result, BSTR *str)
1232 {
1233     HRESULT hr = S_OK;
1234 
1235     if (transform_is_empty_resultdoc(result))
1236         *str = SysAllocStringLen(NULL, 0);
1237     else
1238     {
1239         xmlOutputBufferPtr output = xmlAllocOutputBuffer(xmlFindCharEncodingHandler("UTF-16"));
1240         const xmlChar *content;
1241         size_t len;
1242 
1243         *str = NULL;
1244         if (!output)
1245             return E_OUTOFMEMORY;
1246 
1247         hr = node_transform_write(style, result, TRUE, "UTF-16", output);
1248 #ifdef LIBXML2_NEW_BUFFER
1249         content = xmlBufContent(output->conv);
1250         len = xmlBufUse(output->conv);
1251 #else
1252         content = xmlBufferContent(output->conv);
1253         len = xmlBufferLength(output->conv);
1254 #endif
1255         /* UTF-16 encoder places UTF-16 bom, we don't need it for BSTR */
1256         content += sizeof(WCHAR);
1257         *str = SysAllocStringLen((WCHAR*)content, len/sizeof(WCHAR) - 1);
1258         xmlOutputBufferClose(output);
1259     }
1260 
1261     return *str ? hr : E_OUTOFMEMORY;
1262 }
1263 
1264 static HRESULT node_transform_write_to_stream(xsltStylesheetPtr style, xmlDocPtr result, ISequentialStream *stream)
1265 {
1266     static const xmlChar *utf16 = (const xmlChar*)"UTF-16";
1267     xmlOutputBufferPtr output;
1268     const xmlChar *encoding;
1269     HRESULT hr;
1270 
1271     if (transform_is_empty_resultdoc(result))
1272     {
1273         WARN("empty result document\n");
1274         return S_OK;
1275     }
1276 
1277     if (style->methodURI && (!style->method || !xmlStrEqual(style->method, (const xmlChar *) "xhtml")))
1278     {
1279         ERR("unknown output method\n");
1280         return E_FAIL;
1281     }
1282 
1283     /* default encoding is UTF-16 */
1284     XSLT_GET_IMPORT_PTR(encoding, style, encoding);
1285     if (!encoding)
1286         encoding = utf16;
1287 
1288     output = xmlOutputBufferCreateIO(transform_to_stream_write, NULL, stream, xmlFindCharEncodingHandler((const char*)encoding));
1289     if (!output)
1290         return E_OUTOFMEMORY;
1291 
1292     hr = node_transform_write(style, result, FALSE, (const char*)encoding, output);
1293     xmlOutputBufferClose(output);
1294     return hr;
1295 }
1296 
1297 #endif
1298 
1299 HRESULT node_transform_node_params(const xmlnode *This, IXMLDOMNode *stylesheet, BSTR *p,
1300     ISequentialStream *stream, const struct xslprocessor_params *params)
1301 {
1302 #ifdef SONAME_LIBXSLT
1303     xsltStylesheetPtr xsltSS;
1304     xmlDocPtr sheet_doc;
1305     HRESULT hr = S_OK;
1306     xmlnode *sheet;
1307 
1308     if (!libxslt_handle) return E_NOTIMPL;
1309     if (!stylesheet || !p) return E_INVALIDARG;
1310 
1311     *p = NULL;
1312 
1313     sheet = get_node_obj(stylesheet);
1314     if(!sheet) return E_FAIL;
1315 
1316     sheet_doc = xmlCopyDoc(sheet->node->doc, 1);
1317     xsltSS = pxsltParseStylesheetDoc(sheet_doc);
1318     if (xsltSS)
1319     {
1320         const char **xslparams = NULL;
1321         xmlDocPtr result;
1322         unsigned int i;
1323 
1324         /* convert our parameter list to libxml2 format */
1325         if (params && params->count)
1326         {
1327             struct xslprocessor_par *par;
1328 
1329             i = 0;
1330             xslparams = heap_alloc((params->count*2 + 1)*sizeof(char*));
1331             LIST_FOR_EACH_ENTRY(par, &params->list, struct xslprocessor_par, entry)
1332             {
1333                 xslparams[i++] = (char*)xmlchar_from_wchar(par->name);
1334                 xslparams[i++] = (char*)xmlchar_from_wchar(par->value);
1335             }
1336             xslparams[i] = NULL;
1337         }
1338 
1339         if (xslparams)
1340         {
1341             xsltTransformContextPtr ctxt = pxsltNewTransformContext(xsltSS, This->node->doc);
1342 
1343             /* push parameters to user context */
1344             pxsltQuoteUserParams(ctxt, xslparams);
1345             result = pxsltApplyStylesheetUser(xsltSS, This->node->doc, NULL, NULL, NULL, ctxt);
1346             pxsltFreeTransformContext(ctxt);
1347 
1348             for (i = 0; i < params->count*2; i++)
1349                 heap_free((char*)xslparams[i]);
1350             heap_free(xslparams);
1351         }
1352         else
1353             result = pxsltApplyStylesheet(xsltSS, This->node->doc, NULL);
1354 
1355         if (result)
1356         {
1357             if (stream)
1358                 hr = node_transform_write_to_stream(xsltSS, result, stream);
1359             else
1360                 hr = node_transform_write_to_bstr(xsltSS, result, p);
1361             xmlFreeDoc(result);
1362         }
1363 
1364         pxsltFreeStylesheet(xsltSS);
1365     }
1366     else
1367         xmlFreeDoc(sheet_doc);
1368 
1369     if(!*p) *p = SysAllocStringLen(NULL, 0);
1370 
1371     return hr;
1372 #else
1373     ERR_(winediag)("libxslt headers were not found at compile time. Expect problems.\n");
1374 
1375     return E_NOTIMPL;
1376 #endif
1377 }
1378 
1379 HRESULT node_transform_node(const xmlnode *node, IXMLDOMNode *stylesheet, BSTR *p)
1380 {
1381     return node_transform_node_params(node, stylesheet, p, NULL, NULL);
1382 }
1383 
1384 HRESULT node_select_nodes(const xmlnode *This, BSTR query, IXMLDOMNodeList **nodes)
1385 {
1386     xmlChar* str;
1387     HRESULT hr;
1388 
1389     if (!query || !nodes) return E_INVALIDARG;
1390 
1391     str = xmlchar_from_wchar(query);
1392     hr = create_selection(This->node, str, nodes);
1393     heap_free(str);
1394 
1395     return hr;
1396 }
1397 
1398 HRESULT node_select_singlenode(const xmlnode *This, BSTR query, IXMLDOMNode **node)
1399 {
1400     IXMLDOMNodeList *list;
1401     HRESULT hr;
1402 
1403     hr = node_select_nodes(This, query, &list);
1404     if (hr == S_OK)
1405     {
1406         hr = IXMLDOMNodeList_nextNode(list, node);
1407         IXMLDOMNodeList_Release(list);
1408     }
1409     return hr;
1410 }
1411 
1412 HRESULT node_get_namespaceURI(xmlnode *This, BSTR *namespaceURI)
1413 {
1414     xmlNsPtr ns = This->node->ns;
1415 
1416     if(!namespaceURI)
1417         return E_INVALIDARG;
1418 
1419     *namespaceURI = NULL;
1420 
1421     if (ns && ns->href)
1422         *namespaceURI = bstr_from_xmlChar(ns->href);
1423 
1424     TRACE("uri: %s\n", debugstr_w(*namespaceURI));
1425 
1426     return *namespaceURI ? S_OK : S_FALSE;
1427 }
1428 
1429 HRESULT node_get_prefix(xmlnode *This, BSTR *prefix)
1430 {
1431     xmlNsPtr ns = This->node->ns;
1432 
1433     if (!prefix) return E_INVALIDARG;
1434 
1435     *prefix = NULL;
1436 
1437     if (ns && ns->prefix)
1438         *prefix = bstr_from_xmlChar(ns->prefix);
1439 
1440     TRACE("prefix: %s\n", debugstr_w(*prefix));
1441 
1442     return *prefix ? S_OK : S_FALSE;
1443 }
1444 
1445 HRESULT node_get_base_name(xmlnode *This, BSTR *name)
1446 {
1447     if (!name) return E_INVALIDARG;
1448 
1449     *name = bstr_from_xmlChar(This->node->name);
1450     if (!*name) return E_OUTOFMEMORY;
1451 
1452     TRACE("returning %s\n", debugstr_w(*name));
1453 
1454     return S_OK;
1455 }
1456 
1457 void destroy_xmlnode(xmlnode *This)
1458 {
1459     if(This->node)
1460     {
1461         xmlnode_release(This->node);
1462         xmldoc_release(This->node->doc);
1463     }
1464 }
1465 
1466 void init_xmlnode(xmlnode *This, xmlNodePtr node, IXMLDOMNode *node_iface, dispex_static_data_t *dispex_data)
1467 {
1468     if(node)
1469     {
1470         xmlnode_add_ref(node);
1471         xmldoc_add_ref(node->doc);
1472     }
1473 
1474     This->node = node;
1475     This->iface = node_iface;
1476     This->parent = NULL;
1477 
1478     init_dispex(&This->dispex, (IUnknown*)This->iface, dispex_data);
1479 }
1480 
1481 typedef struct {
1482     xmlnode node;
1483     IXMLDOMNode IXMLDOMNode_iface;
1484     LONG ref;
1485 } unknode;
1486 
1487 static inline unknode *unknode_from_IXMLDOMNode(IXMLDOMNode *iface)
1488 {
1489     return CONTAINING_RECORD(iface, unknode, IXMLDOMNode_iface);
1490 }
1491 
1492 static HRESULT WINAPI unknode_QueryInterface(
1493     IXMLDOMNode *iface,
1494     REFIID riid,
1495     void** ppvObject )
1496 {
1497     unknode *This = unknode_from_IXMLDOMNode( iface );
1498 
1499     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
1500 
1501     if (IsEqualGUID(riid, &IID_IUnknown)) {
1502         *ppvObject = iface;
1503     }else if (IsEqualGUID( riid, &IID_IDispatch) ||
1504               IsEqualGUID( riid, &IID_IXMLDOMNode)) {
1505         *ppvObject = &This->IXMLDOMNode_iface;
1506     }else if(node_query_interface(&This->node, riid, ppvObject)) {
1507         return *ppvObject ? S_OK : E_NOINTERFACE;
1508     }else  {
1509         FIXME("interface %s not implemented\n", debugstr_guid(riid));
1510         *ppvObject = NULL;
1511         return E_NOINTERFACE;
1512     }
1513 
1514     IUnknown_AddRef((IUnknown*)*ppvObject);
1515     return S_OK;
1516 }
1517 
1518 static ULONG WINAPI unknode_AddRef(
1519     IXMLDOMNode *iface )
1520 {
1521     unknode *This = unknode_from_IXMLDOMNode( iface );
1522 
1523     return InterlockedIncrement(&This->ref);
1524 }
1525 
1526 static ULONG WINAPI unknode_Release(
1527     IXMLDOMNode *iface )
1528 {
1529     unknode *This = unknode_from_IXMLDOMNode( iface );
1530     LONG ref;
1531 
1532     ref = InterlockedDecrement( &This->ref );
1533     if(!ref) {
1534         destroy_xmlnode(&This->node);
1535         heap_free(This);
1536     }
1537 
1538     return ref;
1539 }
1540 
1541 static HRESULT WINAPI unknode_GetTypeInfoCount(
1542     IXMLDOMNode *iface,
1543     UINT* pctinfo )
1544 {
1545     unknode *This = unknode_from_IXMLDOMNode( iface );
1546 
1547     TRACE("(%p)->(%p)\n", This, pctinfo);
1548 
1549     *pctinfo = 1;
1550 
1551     return S_OK;
1552 }
1553 
1554 static HRESULT WINAPI unknode_GetTypeInfo(
1555     IXMLDOMNode *iface,
1556     UINT iTInfo,
1557     LCID lcid,
1558     ITypeInfo** ppTInfo )
1559 {
1560     unknode *This = unknode_from_IXMLDOMNode( iface );
1561     HRESULT hr;
1562 
1563     TRACE("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo);
1564 
1565     hr = get_typeinfo(IXMLDOMNode_tid, ppTInfo);
1566 
1567     return hr;
1568 }
1569 
1570 static HRESULT WINAPI unknode_GetIDsOfNames(
1571     IXMLDOMNode *iface,
1572     REFIID riid,
1573     LPOLESTR* rgszNames,
1574     UINT cNames,
1575     LCID lcid,
1576     DISPID* rgDispId )
1577 {
1578     unknode *This = unknode_from_IXMLDOMNode( iface );
1579 
1580     ITypeInfo *typeinfo;
1581     HRESULT hr;
1582 
1583     TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames,
1584           lcid, rgDispId);
1585 
1586     if(!rgszNames || cNames == 0 || !rgDispId)
1587         return E_INVALIDARG;
1588 
1589     hr = get_typeinfo(IXMLDOMNode_tid, &typeinfo);
1590     if(SUCCEEDED(hr))
1591     {
1592         hr = ITypeInfo_GetIDsOfNames(typeinfo, rgszNames, cNames, rgDispId);
1593         ITypeInfo_Release(typeinfo);
1594     }
1595 
1596     return hr;
1597 }
1598 
1599 static HRESULT WINAPI unknode_Invoke(
1600     IXMLDOMNode *iface,
1601     DISPID dispIdMember,
1602     REFIID riid,
1603     LCID lcid,
1604     WORD wFlags,
1605     DISPPARAMS* pDispParams,
1606     VARIANT* pVarResult,
1607     EXCEPINFO* pExcepInfo,
1608     UINT* puArgErr )
1609 {
1610     unknode *This = unknode_from_IXMLDOMNode( iface );
1611     ITypeInfo *typeinfo;
1612     HRESULT hr;
1613 
1614     TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid),
1615           lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
1616 
1617     hr = get_typeinfo(IXMLDOMNode_tid, &typeinfo);
1618     if(SUCCEEDED(hr))
1619     {
1620         hr = ITypeInfo_Invoke(typeinfo, &This->IXMLDOMNode_iface, dispIdMember, wFlags, pDispParams,
1621                 pVarResult, pExcepInfo, puArgErr);
1622         ITypeInfo_Release(typeinfo);
1623     }
1624 
1625     return hr;
1626 }
1627 
1628 static HRESULT WINAPI unknode_get_nodeName(
1629     IXMLDOMNode *iface,
1630     BSTR* p )
1631 {
1632     unknode *This = unknode_from_IXMLDOMNode( iface );
1633 
1634     FIXME("(%p)->(%p)\n", This, p);
1635 
1636     return node_get_nodeName(&This->node, p);
1637 }
1638 
1639 static HRESULT WINAPI unknode_get_nodeValue(
1640     IXMLDOMNode *iface,
1641     VARIANT* value)
1642 {
1643     unknode *This = unknode_from_IXMLDOMNode( iface );
1644 
1645     FIXME("(%p)->(%p)\n", This, value);
1646 
1647     if(!value)
1648         return E_INVALIDARG;
1649 
1650     V_VT(value) = VT_NULL;
1651     return S_FALSE;
1652 }
1653 
1654 static HRESULT WINAPI unknode_put_nodeValue(
1655     IXMLDOMNode *iface,
1656     VARIANT value)
1657 {
1658     unknode *This = unknode_from_IXMLDOMNode( iface );
1659     FIXME("(%p)->(v%d)\n", This, V_VT(&value));
1660     return E_FAIL;
1661 }
1662 
1663 static HRESULT WINAPI unknode_get_nodeType(
1664     IXMLDOMNode *iface,
1665     DOMNodeType* domNodeType )
1666 {
1667     unknode *This = unknode_from_IXMLDOMNode( iface );
1668 
1669     FIXME("(%p)->(%p)\n", This, domNodeType);
1670 
1671     switch (This->node.node->type)
1672     {
1673     case XML_ELEMENT_NODE:
1674     case XML_ATTRIBUTE_NODE:
1675     case XML_TEXT_NODE:
1676     case XML_CDATA_SECTION_NODE:
1677     case XML_ENTITY_REF_NODE:
1678     case XML_ENTITY_NODE:
1679     case XML_PI_NODE:
1680     case XML_COMMENT_NODE:
1681     case XML_DOCUMENT_NODE:
1682     case XML_DOCUMENT_TYPE_NODE:
1683     case XML_DOCUMENT_FRAG_NODE:
1684     case XML_NOTATION_NODE:
1685         /* we only care about this set of types, libxml2 type values are
1686            exactly what we need */
1687         *domNodeType = (DOMNodeType)This->node.node->type;
1688         break;
1689     default:
1690         *domNodeType = NODE_INVALID;
1691         break;
1692     }
1693 
1694     return S_OK;
1695 }
1696 
1697 static HRESULT WINAPI unknode_get_parentNode(
1698     IXMLDOMNode *iface,
1699     IXMLDOMNode** parent )
1700 {
1701     unknode *This = unknode_from_IXMLDOMNode( iface );
1702     FIXME("(%p)->(%p)\n", This, parent);
1703     if (!parent) return E_INVALIDARG;
1704     *parent = NULL;
1705     return S_FALSE;
1706 }
1707 
1708 static HRESULT WINAPI unknode_get_childNodes(
1709     IXMLDOMNode *iface,
1710     IXMLDOMNodeList** outList)
1711 {
1712     unknode *This = unknode_from_IXMLDOMNode( iface );
1713 
1714     TRACE("(%p)->(%p)\n", This, outList);
1715 
1716     return node_get_child_nodes(&This->node, outList);
1717 }
1718 
1719 static HRESULT WINAPI unknode_get_firstChild(
1720     IXMLDOMNode *iface,
1721     IXMLDOMNode** domNode)
1722 {
1723     unknode *This = unknode_from_IXMLDOMNode( iface );
1724 
1725     TRACE("(%p)->(%p)\n", This, domNode);
1726 
1727     return node_get_first_child(&This->node, domNode);
1728 }
1729 
1730 static HRESULT WINAPI unknode_get_lastChild(
1731     IXMLDOMNode *iface,
1732     IXMLDOMNode** domNode)
1733 {
1734     unknode *This = unknode_from_IXMLDOMNode( iface );
1735 
1736     TRACE("(%p)->(%p)\n", This, domNode);
1737 
1738     return node_get_last_child(&This->node, domNode);
1739 }
1740 
1741 static HRESULT WINAPI unknode_get_previousSibling(
1742     IXMLDOMNode *iface,
1743     IXMLDOMNode** domNode)
1744 {
1745     unknode *This = unknode_from_IXMLDOMNode( iface );
1746 
1747     TRACE("(%p)->(%p)\n", This, domNode);
1748 
1749     return node_get_previous_sibling(&This->node, domNode);
1750 }
1751 
1752 static HRESULT WINAPI unknode_get_nextSibling(
1753     IXMLDOMNode *iface,
1754     IXMLDOMNode** domNode)
1755 {
1756     unknode *This = unknode_from_IXMLDOMNode( iface );
1757 
1758     TRACE("(%p)->(%p)\n", This, domNode);
1759 
1760     return node_get_next_sibling(&This->node, domNode);
1761 }
1762 
1763 static HRESULT WINAPI unknode_get_attributes(
1764     IXMLDOMNode *iface,
1765     IXMLDOMNamedNodeMap** attributeMap)
1766 {
1767     unknode *This = unknode_from_IXMLDOMNode( iface );
1768 
1769     FIXME("(%p)->(%p)\n", This, attributeMap);
1770 
1771     return return_null_ptr((void**)attributeMap);
1772 }
1773 
1774 static HRESULT WINAPI unknode_insertBefore(
1775     IXMLDOMNode *iface,
1776     IXMLDOMNode* newNode, VARIANT refChild,
1777     IXMLDOMNode** outOldNode)
1778 {
1779     unknode *This = unknode_from_IXMLDOMNode( iface );
1780 
1781     FIXME("(%p)->(%p x%d %p)\n", This, newNode, V_VT(&refChild), outOldNode);
1782 
1783     return node_insert_before(&This->node, newNode, &refChild, outOldNode);
1784 }
1785 
1786 static HRESULT WINAPI unknode_replaceChild(
1787     IXMLDOMNode *iface,
1788     IXMLDOMNode* newNode,
1789     IXMLDOMNode* oldNode,
1790     IXMLDOMNode** outOldNode)
1791 {
1792     unknode *This = unknode_from_IXMLDOMNode( iface );
1793 
1794     FIXME("(%p)->(%p %p %p)\n", This, newNode, oldNode, outOldNode);
1795 
1796     return node_replace_child(&This->node, newNode, oldNode, outOldNode);
1797 }
1798 
1799 static HRESULT WINAPI unknode_removeChild(
1800     IXMLDOMNode *iface,
1801     IXMLDOMNode* domNode, IXMLDOMNode** oldNode)
1802 {
1803     unknode *This = unknode_from_IXMLDOMNode( iface );
1804     return node_remove_child(&This->node, domNode, oldNode);
1805 }
1806 
1807 static HRESULT WINAPI unknode_appendChild(
1808     IXMLDOMNode *iface,
1809     IXMLDOMNode* newNode, IXMLDOMNode** outNewNode)
1810 {
1811     unknode *This = unknode_from_IXMLDOMNode( iface );
1812     return node_append_child(&This->node, newNode, outNewNode);
1813 }
1814 
1815 static HRESULT WINAPI unknode_hasChildNodes(
1816     IXMLDOMNode *iface,
1817     VARIANT_BOOL* pbool)
1818 {
1819     unknode *This = unknode_from_IXMLDOMNode( iface );
1820     return node_has_childnodes(&This->node, pbool);
1821 }
1822 
1823 static HRESULT WINAPI unknode_get_ownerDocument(
1824     IXMLDOMNode *iface,
1825     IXMLDOMDocument** domDocument)
1826 {
1827     unknode *This = unknode_from_IXMLDOMNode( iface );
1828     return node_get_owner_doc(&This->node, domDocument);
1829 }
1830 
1831 static HRESULT WINAPI unknode_cloneNode(
1832     IXMLDOMNode *iface,
1833     VARIANT_BOOL pbool, IXMLDOMNode** outNode)
1834 {
1835     unknode *This = unknode_from_IXMLDOMNode( iface );
1836     return node_clone(&This->node, pbool, outNode );
1837 }
1838 
1839 static HRESULT WINAPI unknode_get_nodeTypeString(
1840     IXMLDOMNode *iface,
1841     BSTR* p)
1842 {
1843     unknode *This = unknode_from_IXMLDOMNode( iface );
1844 
1845     FIXME("(%p)->(%p)\n", This, p);
1846 
1847     return node_get_nodeName(&This->node, p);
1848 }
1849 
1850 static HRESULT WINAPI unknode_get_text(
1851     IXMLDOMNode *iface,
1852     BSTR* p)
1853 {
1854     unknode *This = unknode_from_IXMLDOMNode( iface );
1855     return node_get_text(&This->node, p);
1856 }
1857 
1858 static HRESULT WINAPI unknode_put_text(
1859     IXMLDOMNode *iface,
1860     BSTR p)
1861 {
1862     unknode *This = unknode_from_IXMLDOMNode( iface );
1863     return node_put_text(&This->node, p);
1864 }
1865 
1866 static HRESULT WINAPI unknode_get_specified(
1867     IXMLDOMNode *iface,
1868     VARIANT_BOOL* isSpecified)
1869 {
1870     unknode *This = unknode_from_IXMLDOMNode( iface );
1871     FIXME("(%p)->(%p) stub!\n", This, isSpecified);
1872     *isSpecified = VARIANT_TRUE;
1873     return S_OK;
1874 }
1875 
1876 static HRESULT WINAPI unknode_get_definition(
1877     IXMLDOMNode *iface,
1878     IXMLDOMNode** definitionNode)
1879 {
1880     unknode *This = unknode_from_IXMLDOMNode( iface );
1881     FIXME("(%p)->(%p)\n", This, definitionNode);
1882     return E_NOTIMPL;
1883 }
1884 
1885 static HRESULT WINAPI unknode_get_nodeTypedValue(
1886     IXMLDOMNode *iface,
1887     VARIANT* var1)
1888 {
1889     unknode *This = unknode_from_IXMLDOMNode( iface );
1890     FIXME("(%p)->(%p)\n", This, var1);
1891     return return_null_var(var1);
1892 }
1893 
1894 static HRESULT WINAPI unknode_put_nodeTypedValue(
1895     IXMLDOMNode *iface,
1896     VARIANT typedValue)
1897 {
1898     unknode *This = unknode_from_IXMLDOMNode( iface );
1899     FIXME("(%p)->(%s)\n", This, debugstr_variant(&typedValue));
1900     return E_NOTIMPL;
1901 }
1902 
1903 static HRESULT WINAPI unknode_get_dataType(
1904     IXMLDOMNode *iface,
1905     VARIANT* var1)
1906 {
1907     unknode *This = unknode_from_IXMLDOMNode( iface );
1908     TRACE("(%p)->(%p)\n", This, var1);
1909     return return_null_var(var1);
1910 }
1911 
1912 static HRESULT WINAPI unknode_put_dataType(
1913     IXMLDOMNode *iface,
1914     BSTR p)
1915 {
1916     unknode *This = unknode_from_IXMLDOMNode( iface );
1917 
1918     FIXME("(%p)->(%s)\n", This, debugstr_w(p));
1919 
1920     if(!p)
1921         return E_INVALIDARG;
1922 
1923     return E_FAIL;
1924 }
1925 
1926 static HRESULT WINAPI unknode_get_xml(
1927     IXMLDOMNode *iface,
1928     BSTR* p)
1929 {
1930     unknode *This = unknode_from_IXMLDOMNode( iface );
1931 
1932     FIXME("(%p)->(%p)\n", This, p);
1933 
1934     return node_get_xml(&This->node, FALSE, p);
1935 }
1936 
1937 static HRESULT WINAPI unknode_transformNode(
1938     IXMLDOMNode *iface,
1939     IXMLDOMNode* domNode, BSTR* p)
1940 {
1941     unknode *This = unknode_from_IXMLDOMNode( iface );
1942     return node_transform_node(&This->node, domNode, p);
1943 }
1944 
1945 static HRESULT WINAPI unknode_selectNodes(
1946     IXMLDOMNode *iface,
1947     BSTR p, IXMLDOMNodeList** outList)
1948 {
1949     unknode *This = unknode_from_IXMLDOMNode( iface );
1950     return node_select_nodes(&This->node, p, outList);
1951 }
1952 
1953 static HRESULT WINAPI unknode_selectSingleNode(
1954     IXMLDOMNode *iface,
1955     BSTR p, IXMLDOMNode** outNode)
1956 {
1957     unknode *This = unknode_from_IXMLDOMNode( iface );
1958     return node_select_singlenode(&This->node, p, outNode);
1959 }
1960 
1961 static HRESULT WINAPI unknode_get_parsed(
1962     IXMLDOMNode *iface,
1963     VARIANT_BOOL* isParsed)
1964 {
1965     unknode *This = unknode_from_IXMLDOMNode( iface );
1966     FIXME("(%p)->(%p) stub!\n", This, isParsed);
1967     *isParsed = VARIANT_TRUE;
1968     return S_OK;
1969 }
1970 
1971 static HRESULT WINAPI unknode_get_namespaceURI(
1972     IXMLDOMNode *iface,
1973     BSTR* p)
1974 {
1975     unknode *This = unknode_from_IXMLDOMNode( iface );
1976     TRACE("(%p)->(%p)\n", This, p);
1977     return node_get_namespaceURI(&This->node, p);
1978 }
1979 
1980 static HRESULT WINAPI unknode_get_prefix(
1981     IXMLDOMNode *iface,
1982     BSTR* p)
1983 {
1984     unknode *This = unknode_from_IXMLDOMNode( iface );
1985     return node_get_prefix(&This->node, p);
1986 }
1987 
1988 static HRESULT WINAPI unknode_get_baseName(
1989     IXMLDOMNode *iface,
1990     BSTR* p)
1991 {
1992     unknode *This = unknode_from_IXMLDOMNode( iface );
1993     return node_get_base_name(&This->node, p);
1994 }
1995 
1996 static HRESULT WINAPI unknode_transformNodeToObject(
1997     IXMLDOMNode *iface,
1998     IXMLDOMNode* domNode, VARIANT var1)
1999 {
2000     unknode *This = unknode_from_IXMLDOMNode( iface );
2001     FIXME("(%p)->(%p %s)\n", This, domNode, debugstr_variant(&var1));
2002     return E_NOTIMPL;
2003 }
2004 
2005 static const struct IXMLDOMNodeVtbl unknode_vtbl =
2006 {
2007     unknode_QueryInterface,
2008     unknode_AddRef,
2009     unknode_Release,
2010     unknode_GetTypeInfoCount,
2011     unknode_GetTypeInfo,
2012     unknode_GetIDsOfNames,
2013     unknode_Invoke,
2014     unknode_get_nodeName,
2015     unknode_get_nodeValue,
2016     unknode_put_nodeValue,
2017     unknode_get_nodeType,
2018     unknode_get_parentNode,
2019     unknode_get_childNodes,
2020     unknode_get_firstChild,
2021     unknode_get_lastChild,
2022     unknode_get_previousSibling,
2023     unknode_get_nextSibling,
2024     unknode_get_attributes,
2025     unknode_insertBefore,
2026     unknode_replaceChild,
2027     unknode_removeChild,
2028     unknode_appendChild,
2029     unknode_hasChildNodes,
2030     unknode_get_ownerDocument,
2031     unknode_cloneNode,
2032     unknode_get_nodeTypeString,
2033     unknode_get_text,
2034     unknode_put_text,
2035     unknode_get_specified,
2036     unknode_get_definition,
2037     unknode_get_nodeTypedValue,
2038     unknode_put_nodeTypedValue,
2039     unknode_get_dataType,
2040     unknode_put_dataType,
2041     unknode_get_xml,
2042     unknode_transformNode,
2043     unknode_selectNodes,
2044     unknode_selectSingleNode,
2045     unknode_get_parsed,
2046     unknode_get_namespaceURI,
2047     unknode_get_prefix,
2048     unknode_get_baseName,
2049     unknode_transformNodeToObject
2050 };
2051 
2052 IXMLDOMNode *create_node( xmlNodePtr node )
2053 {
2054     IUnknown *pUnk;
2055     IXMLDOMNode *ret;
2056     HRESULT hr;
2057 
2058     if ( !node )
2059         return NULL;
2060 
2061     TRACE("type %d\n", node->type);
2062     switch(node->type)
2063     {
2064     case XML_ELEMENT_NODE:
2065         pUnk = create_element( node );
2066         break;
2067     case XML_ATTRIBUTE_NODE:
2068         pUnk = create_attribute( node );
2069         break;
2070     case XML_TEXT_NODE:
2071         pUnk = create_text( node );
2072         break;
2073     case XML_CDATA_SECTION_NODE:
2074         pUnk = create_cdata( node );
2075         break;
2076     case XML_ENTITY_REF_NODE:
2077         pUnk = create_doc_entity_ref( node );
2078         break;
2079     case XML_PI_NODE:
2080         pUnk = create_pi( node );
2081         break;
2082     case XML_COMMENT_NODE:
2083         pUnk = create_comment( node );
2084         break;
2085     case XML_DOCUMENT_NODE:
2086         pUnk = create_domdoc( node );
2087         break;
2088     case XML_DOCUMENT_FRAG_NODE:
2089         pUnk = create_doc_fragment( node );
2090         break;
2091     case XML_DTD_NODE:
2092     case XML_DOCUMENT_TYPE_NODE:
2093         pUnk = create_doc_type( node );
2094         break;
2095     case XML_ENTITY_NODE:
2096     case XML_NOTATION_NODE: {
2097         unknode *new_node;
2098 
2099         FIXME("only creating basic node for type %d\n", node->type);
2100 
2101         new_node = heap_alloc(sizeof(unknode));
2102         if(!new_node)
2103             return NULL;
2104 
2105         new_node->IXMLDOMNode_iface.lpVtbl = &unknode_vtbl;
2106         new_node->ref = 1;
2107         init_xmlnode(&new_node->node, node, &new_node->IXMLDOMNode_iface, NULL);
2108         pUnk = (IUnknown*)&new_node->IXMLDOMNode_iface;
2109         break;
2110     }
2111     default:
2112         ERR("Called for unsupported node type %d\n", node->type);
2113         return NULL;
2114     }
2115 
2116     hr = IUnknown_QueryInterface(pUnk, &IID_IXMLDOMNode, (LPVOID*)&ret);
2117     IUnknown_Release(pUnk);
2118     if(FAILED(hr)) return NULL;
2119     return ret;
2120 }
2121 #endif
2122