xmllite: Implement PI parsing.
[wine] / dlls / msxml3 / selection.c
1 /*
2  *    XPath/XSLPattern query result node list implementation
3  *
4  * Copyright 2005 Mike McCormack
5  * Copyright 2007 Mikolaj Zalewski
6  * Copyright 2010 Adam Martinson for CodeWeavers
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #define COBJMACROS
24
25 #include "config.h"
26
27 #include <stdarg.h>
28 #ifdef HAVE_LIBXML2
29 # include <libxml/parser.h>
30 # include <libxml/xmlerror.h>
31 # include <libxml/xpath.h>
32 # include <libxml/xpathInternals.h>
33 #endif
34
35 #include "windef.h"
36 #include "winbase.h"
37 #include "winuser.h"
38 #include "ole2.h"
39 #include "msxml6.h"
40 #include "msxml2did.h"
41
42 #include "msxml_private.h"
43
44 #include "wine/debug.h"
45
46 /* This file implements the object returned by a XPath query. Note that this is
47  * not the IXMLDOMNodeList returned by childNodes - it's implemented in nodelist.c.
48  * They are different because the list returned by XPath queries:
49  *  - is static - gives the results for the XML tree as it existed during the
50  *    execution of the query
51  *  - supports IXMLDOMSelection
52  *
53  */
54
55 WINE_DEFAULT_DEBUG_CHANNEL(msxml);
56
57 #ifdef HAVE_LIBXML2
58
59 int registerNamespaces(xmlXPathContextPtr ctxt);
60 xmlChar* XSLPattern_to_XPath(xmlXPathContextPtr ctxt, xmlChar const* xslpat_str);
61
62 typedef struct
63 {
64     IEnumVARIANT IEnumVARIANT_iface;
65     LONG ref;
66
67     IUnknown *outer;
68     BOOL own;
69
70     LONG pos;
71
72     const struct enumvariant_funcs *funcs;
73 } enumvariant;
74
75 typedef struct
76 {
77     DispatchEx dispex;
78     IXMLDOMSelection IXMLDOMSelection_iface;
79     LONG ref;
80     xmlNodePtr node;
81     xmlXPathObjectPtr result;
82     int resultPos;
83     IEnumVARIANT *enumvariant;
84 } domselection;
85
86 static HRESULT selection_get_item(IUnknown *iface, LONG index, VARIANT* item)
87 {
88     V_VT(item) = VT_DISPATCH;
89     return IXMLDOMSelection_get_item((IXMLDOMSelection*)iface, index, (IXMLDOMNode**)&V_DISPATCH(item));
90 }
91
92 static HRESULT selection_next(IUnknown *iface)
93 {
94     IXMLDOMNode *node;
95     HRESULT hr = IXMLDOMSelection_nextNode((IXMLDOMSelection*)iface, &node);
96     if (hr == S_OK) IXMLDOMNode_Release(node);
97     return hr;
98 }
99
100 static const struct enumvariant_funcs selection_enumvariant = {
101     selection_get_item,
102     selection_next
103 };
104
105 static inline domselection *impl_from_IXMLDOMSelection( IXMLDOMSelection *iface )
106 {
107     return CONTAINING_RECORD(iface, domselection, IXMLDOMSelection_iface);
108 }
109
110 static inline enumvariant *impl_from_IEnumVARIANT( IEnumVARIANT *iface )
111 {
112     return CONTAINING_RECORD(iface, enumvariant, IEnumVARIANT_iface);
113 }
114
115 static HRESULT WINAPI domselection_QueryInterface(
116     IXMLDOMSelection *iface,
117     REFIID riid,
118     void** ppvObject )
119 {
120     domselection *This = impl_from_IXMLDOMSelection( iface );
121
122     TRACE("(%p)->(%s %p)\n", iface, debugstr_guid(riid), ppvObject);
123
124     if(!ppvObject)
125         return E_INVALIDARG;
126
127     if ( IsEqualGUID( riid, &IID_IUnknown ) ||
128          IsEqualGUID( riid, &IID_IXMLDOMNodeList ) ||
129          IsEqualGUID( riid, &IID_IXMLDOMSelection ))
130     {
131         *ppvObject = &This->IXMLDOMSelection_iface;
132     }
133     else if (IsEqualGUID( riid, &IID_IEnumVARIANT ))
134     {
135         if (!This->enumvariant)
136         {
137             HRESULT hr = create_enumvariant((IUnknown*)iface, FALSE, &selection_enumvariant, &This->enumvariant);
138             if (FAILED(hr)) return hr;
139         }
140
141         return IEnumVARIANT_QueryInterface(This->enumvariant, &IID_IEnumVARIANT, ppvObject);
142     }
143     else if (dispex_query_interface(&This->dispex, riid, ppvObject))
144     {
145         return *ppvObject ? S_OK : E_NOINTERFACE;
146     }
147     else
148     {
149         TRACE("interface %s not implemented\n", debugstr_guid(riid));
150         *ppvObject = NULL;
151         return E_NOINTERFACE;
152     }
153
154     IXMLDOMSelection_AddRef( iface );
155
156     return S_OK;
157 }
158
159 static ULONG WINAPI domselection_AddRef(
160     IXMLDOMSelection *iface )
161 {
162     domselection *This = impl_from_IXMLDOMSelection( iface );
163     ULONG ref = InterlockedIncrement( &This->ref );
164     TRACE("(%p)->(%d)\n", This, ref);
165     return ref;
166 }
167
168 static ULONG WINAPI domselection_Release(
169     IXMLDOMSelection *iface )
170 {
171     domselection *This = impl_from_IXMLDOMSelection( iface );
172     ULONG ref = InterlockedDecrement(&This->ref);
173
174     TRACE("(%p)->(%d)\n", This, ref);
175     if ( ref == 0 )
176     {
177         xmlXPathFreeObject(This->result);
178         xmldoc_release(This->node->doc);
179         if (This->enumvariant) IEnumVARIANT_Release(This->enumvariant);
180         release_dispex(&This->dispex);
181         heap_free(This);
182     }
183
184     return ref;
185 }
186
187 static HRESULT WINAPI domselection_GetTypeInfoCount(
188     IXMLDOMSelection *iface,
189     UINT* pctinfo )
190 {
191     domselection *This = impl_from_IXMLDOMSelection( iface );
192     return IDispatchEx_GetTypeInfoCount(&This->dispex.IDispatchEx_iface, pctinfo);
193 }
194
195 static HRESULT WINAPI domselection_GetTypeInfo(
196     IXMLDOMSelection *iface,
197     UINT iTInfo,
198     LCID lcid,
199     ITypeInfo** ppTInfo )
200 {
201     domselection *This = impl_from_IXMLDOMSelection( iface );
202     return IDispatchEx_GetTypeInfo(&This->dispex.IDispatchEx_iface,
203         iTInfo, lcid, ppTInfo);
204 }
205
206 static HRESULT WINAPI domselection_GetIDsOfNames(
207     IXMLDOMSelection *iface,
208     REFIID riid,
209     LPOLESTR* rgszNames,
210     UINT cNames,
211     LCID lcid,
212     DISPID* rgDispId )
213 {
214     domselection *This = impl_from_IXMLDOMSelection( iface );
215     return IDispatchEx_GetIDsOfNames(&This->dispex.IDispatchEx_iface,
216         riid, rgszNames, cNames, lcid, rgDispId);
217 }
218
219 static HRESULT WINAPI domselection_Invoke(
220     IXMLDOMSelection *iface,
221     DISPID dispIdMember,
222     REFIID riid,
223     LCID lcid,
224     WORD wFlags,
225     DISPPARAMS* pDispParams,
226     VARIANT* pVarResult,
227     EXCEPINFO* pExcepInfo,
228     UINT* puArgErr )
229 {
230     domselection *This = impl_from_IXMLDOMSelection( iface );
231     return IDispatchEx_Invoke(&This->dispex.IDispatchEx_iface,
232         dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
233 }
234
235 static HRESULT WINAPI domselection_get_item(
236         IXMLDOMSelection* iface,
237         LONG index,
238         IXMLDOMNode** listItem)
239 {
240     domselection *This = impl_from_IXMLDOMSelection( iface );
241
242     TRACE("(%p)->(%d %p)\n", This, index, listItem);
243
244     if(!listItem)
245         return E_INVALIDARG;
246
247     *listItem = NULL;
248
249     if (index < 0 || index >= xmlXPathNodeSetGetLength(This->result->nodesetval))
250         return S_FALSE;
251
252     *listItem = create_node(xmlXPathNodeSetItem(This->result->nodesetval, index));
253     This->resultPos = index + 1;
254
255     return S_OK;
256 }
257
258 static HRESULT WINAPI domselection_get_length(
259         IXMLDOMSelection* iface,
260         LONG* listLength)
261 {
262     domselection *This = impl_from_IXMLDOMSelection( iface );
263
264     TRACE("(%p)->(%p)\n", This, listLength);
265
266     if(!listLength)
267         return E_INVALIDARG;
268
269     *listLength = xmlXPathNodeSetGetLength(This->result->nodesetval);
270     return S_OK;
271 }
272
273 static HRESULT WINAPI domselection_nextNode(
274         IXMLDOMSelection* iface,
275         IXMLDOMNode** nextItem)
276 {
277     domselection *This = impl_from_IXMLDOMSelection( iface );
278
279     TRACE("(%p)->(%p)\n", This, nextItem );
280
281     if(!nextItem)
282         return E_INVALIDARG;
283
284     *nextItem = NULL;
285
286     if (This->resultPos >= xmlXPathNodeSetGetLength(This->result->nodesetval))
287         return S_FALSE;
288
289     *nextItem = create_node(xmlXPathNodeSetItem(This->result->nodesetval, This->resultPos));
290     This->resultPos++;
291     return S_OK;
292 }
293
294 static HRESULT WINAPI domselection_reset(
295         IXMLDOMSelection* iface)
296 {
297     domselection *This = impl_from_IXMLDOMSelection( iface );
298
299     TRACE("%p\n", This);
300     This->resultPos = 0;
301     return S_OK;
302 }
303
304 static HRESULT WINAPI domselection_get__newEnum(
305         IXMLDOMSelection* iface,
306         IUnknown** enumv)
307 {
308     domselection *This = impl_from_IXMLDOMSelection( iface );
309
310     TRACE("(%p)->(%p)\n", This, enumv);
311
312     return create_enumvariant((IUnknown*)iface, TRUE, &selection_enumvariant, (IEnumVARIANT**)enumv);
313 }
314
315 static HRESULT WINAPI domselection_get_expr(
316         IXMLDOMSelection* iface,
317         BSTR *p)
318 {
319     domselection *This = impl_from_IXMLDOMSelection( iface );
320     FIXME("(%p)->(%p)\n", This, p);
321     return E_NOTIMPL;
322 }
323
324 static HRESULT WINAPI domselection_put_expr(
325         IXMLDOMSelection* iface,
326         BSTR p)
327 {
328     domselection *This = impl_from_IXMLDOMSelection( iface );
329     FIXME("(%p)->(%s)\n", This, debugstr_w(p));
330     return E_NOTIMPL;
331 }
332
333 static HRESULT WINAPI domselection_get_context(
334         IXMLDOMSelection* iface,
335         IXMLDOMNode **node)
336 {
337     domselection *This = impl_from_IXMLDOMSelection( iface );
338     FIXME("(%p)->(%p)\n", This, node);
339     return E_NOTIMPL;
340 }
341
342 static HRESULT WINAPI domselection_putref_context(
343         IXMLDOMSelection* iface,
344         IXMLDOMNode *node)
345 {
346     domselection *This = impl_from_IXMLDOMSelection( iface );
347     FIXME("(%p)->(%p)\n", This, node);
348     return E_NOTIMPL;
349 }
350
351 static HRESULT WINAPI domselection_peekNode(
352         IXMLDOMSelection* iface,
353         IXMLDOMNode **node)
354 {
355     domselection *This = impl_from_IXMLDOMSelection( iface );
356     FIXME("(%p)->(%p)\n", This, node);
357     return E_NOTIMPL;
358 }
359
360 static HRESULT WINAPI domselection_matches(
361         IXMLDOMSelection* iface,
362         IXMLDOMNode *node,
363         IXMLDOMNode **out_node)
364 {
365     domselection *This = impl_from_IXMLDOMSelection( iface );
366     FIXME("(%p)->(%p %p)\n", This, node, out_node);
367     return E_NOTIMPL;
368 }
369
370 static HRESULT WINAPI domselection_removeNext(
371         IXMLDOMSelection* iface,
372         IXMLDOMNode **node)
373 {
374     domselection *This = impl_from_IXMLDOMSelection( iface );
375     FIXME("(%p)->(%p)\n", This, node);
376     return E_NOTIMPL;
377 }
378
379 static HRESULT WINAPI domselection_removeAll(
380         IXMLDOMSelection* iface)
381 {
382     domselection *This = impl_from_IXMLDOMSelection( iface );
383     FIXME("(%p)\n", This);
384     return E_NOTIMPL;
385 }
386
387 static HRESULT WINAPI domselection_clone(
388         IXMLDOMSelection* iface,
389         IXMLDOMSelection **node)
390 {
391     domselection *This = impl_from_IXMLDOMSelection( iface );
392     FIXME("(%p)->(%p)\n", This, node);
393     return E_NOTIMPL;
394 }
395
396 static HRESULT WINAPI domselection_getProperty(
397         IXMLDOMSelection* iface,
398         BSTR p,
399         VARIANT *var)
400 {
401     domselection *This = impl_from_IXMLDOMSelection( iface );
402     FIXME("(%p)->(%s %p)\n", This, debugstr_w(p), var);
403     return E_NOTIMPL;
404 }
405
406 static HRESULT WINAPI domselection_setProperty(
407         IXMLDOMSelection* iface,
408         BSTR p,
409         VARIANT var)
410 {
411     domselection *This = impl_from_IXMLDOMSelection( iface );
412     FIXME("(%p)->(%s %s)\n", This, debugstr_w(p), debugstr_variant(&var));
413     return E_NOTIMPL;
414 }
415
416 static const struct IXMLDOMSelectionVtbl domselection_vtbl =
417 {
418     domselection_QueryInterface,
419     domselection_AddRef,
420     domselection_Release,
421     domselection_GetTypeInfoCount,
422     domselection_GetTypeInfo,
423     domselection_GetIDsOfNames,
424     domselection_Invoke,
425     domselection_get_item,
426     domselection_get_length,
427     domselection_nextNode,
428     domselection_reset,
429     domselection_get__newEnum,
430     domselection_get_expr,
431     domselection_put_expr,
432     domselection_get_context,
433     domselection_putref_context,
434     domselection_peekNode,
435     domselection_matches,
436     domselection_removeNext,
437     domselection_removeAll,
438     domselection_clone,
439     domselection_getProperty,
440     domselection_setProperty
441 };
442
443 /* IEnumVARIANT support */
444 static HRESULT WINAPI enumvariant_QueryInterface(
445     IEnumVARIANT *iface,
446     REFIID riid,
447     void** ppvObject )
448 {
449     enumvariant *This = impl_from_IEnumVARIANT( iface );
450
451     TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppvObject);
452
453     *ppvObject = NULL;
454
455     if (IsEqualGUID( riid, &IID_IUnknown ))
456     {
457         if (This->own)
458             *ppvObject = &This->IEnumVARIANT_iface;
459         else
460             return IUnknown_QueryInterface(This->outer, riid, ppvObject);
461     }
462     else if (IsEqualGUID( riid, &IID_IEnumVARIANT ))
463     {
464         *ppvObject = &This->IEnumVARIANT_iface;
465     }
466     else
467         return IUnknown_QueryInterface(This->outer, riid, ppvObject);
468
469     IEnumVARIANT_AddRef( iface );
470
471     return S_OK;
472 }
473
474 static ULONG WINAPI enumvariant_AddRef(IEnumVARIANT *iface )
475 {
476     enumvariant *This = impl_from_IEnumVARIANT( iface );
477     ULONG ref = InterlockedIncrement( &This->ref );
478     TRACE("(%p)->(%d)\n", This, ref);
479     return ref;
480 }
481
482 static ULONG WINAPI enumvariant_Release(IEnumVARIANT *iface )
483 {
484     enumvariant *This = impl_from_IEnumVARIANT( iface );
485     ULONG ref = InterlockedDecrement(&This->ref);
486
487     TRACE("(%p)->(%d)\n", This, ref);
488     if ( ref == 0 )
489     {
490         if (This->own) IUnknown_Release(This->outer);
491         heap_free(This);
492     }
493
494     return ref;
495 }
496
497 static HRESULT WINAPI enumvariant_Next(
498     IEnumVARIANT *iface,
499     ULONG celt,
500     VARIANT *var,
501     ULONG *fetched)
502 {
503     enumvariant *This = impl_from_IEnumVARIANT( iface );
504     ULONG ret_count = 0;
505
506     TRACE("(%p)->(%u %p %p)\n", This, celt, var, fetched);
507
508     if (fetched) *fetched = 0;
509
510     if (celt && !var) return E_INVALIDARG;
511
512     for (; celt > 0; celt--, var++, This->pos++)
513     {
514         HRESULT hr = This->funcs->get_item(This->outer, This->pos, var);
515         if (hr != S_OK)
516         {
517             V_VT(var) = VT_EMPTY;
518             break;
519         }
520         ret_count++;
521     }
522
523     if (fetched) (*fetched)++;
524
525     /* we need to advance one step more for some reason */
526     if (ret_count)
527     {
528         if (This->funcs->next)
529             This->funcs->next(This->outer);
530     }
531
532     return celt == 0 ? S_OK : S_FALSE;
533 }
534
535 static HRESULT WINAPI enumvariant_Skip(
536     IEnumVARIANT *iface,
537     ULONG celt)
538 {
539     enumvariant *This = impl_from_IEnumVARIANT( iface );
540     FIXME("(%p)->(%u): stub\n", This, celt);
541     return E_NOTIMPL;
542 }
543
544 static HRESULT WINAPI enumvariant_Reset(IEnumVARIANT *iface)
545 {
546     enumvariant *This = impl_from_IEnumVARIANT( iface );
547     FIXME("(%p): stub\n", This);
548     return E_NOTIMPL;
549 }
550
551 static HRESULT WINAPI enumvariant_Clone(
552     IEnumVARIANT *iface, IEnumVARIANT **ppenum)
553 {
554     enumvariant *This = impl_from_IEnumVARIANT( iface );
555     FIXME("(%p)->(%p): stub\n", This, ppenum);
556     return E_NOTIMPL;
557 }
558
559 static const struct IEnumVARIANTVtbl EnumVARIANTVtbl =
560 {
561     enumvariant_QueryInterface,
562     enumvariant_AddRef,
563     enumvariant_Release,
564     enumvariant_Next,
565     enumvariant_Skip,
566     enumvariant_Reset,
567     enumvariant_Clone
568 };
569
570 HRESULT create_enumvariant(IUnknown *outer, BOOL own, const struct enumvariant_funcs *funcs, IEnumVARIANT **penum)
571 {
572     enumvariant *This;
573
574     This = heap_alloc(sizeof(enumvariant));
575     if (!This) return E_OUTOFMEMORY;
576
577     This->IEnumVARIANT_iface.lpVtbl = &EnumVARIANTVtbl;
578     This->ref = 0;
579     This->outer = outer;
580     This->own = own;
581     This->pos = 0;
582     This->funcs = funcs;
583
584     if (This->own)
585         IUnknown_AddRef(This->outer);
586
587     *penum = &This->IEnumVARIANT_iface;
588     IEnumVARIANT_AddRef(*penum);
589     return S_OK;
590 }
591
592 static HRESULT domselection_get_dispid(IUnknown *iface, BSTR name, DWORD flags, DISPID *dispid)
593 {
594     WCHAR *ptr;
595     int idx = 0;
596
597     for(ptr = name; *ptr && isdigitW(*ptr); ptr++)
598         idx = idx*10 + (*ptr-'0');
599     if(*ptr)
600         return DISP_E_UNKNOWNNAME;
601
602     *dispid = DISPID_DOM_COLLECTION_BASE + idx;
603     TRACE("ret %x\n", *dispid);
604     return S_OK;
605 }
606
607 static HRESULT domselection_invoke(IUnknown *iface, DISPID id, LCID lcid, WORD flags, DISPPARAMS *params,
608         VARIANT *res, EXCEPINFO *ei)
609 {
610     domselection *This = impl_from_IXMLDOMSelection( (IXMLDOMSelection*)iface );
611
612     TRACE("(%p)->(%x %x %x %p %p %p)\n", This, id, lcid, flags, params, res, ei);
613
614     V_VT(res) = VT_DISPATCH;
615     V_DISPATCH(res) = NULL;
616
617     if (id < DISPID_DOM_COLLECTION_BASE || id > DISPID_DOM_COLLECTION_MAX)
618         return DISP_E_UNKNOWNNAME;
619
620     switch(flags)
621     {
622         case INVOKE_PROPERTYGET:
623         {
624             IXMLDOMNode *disp = NULL;
625
626             IXMLDOMSelection_get_item(&This->IXMLDOMSelection_iface, id - DISPID_DOM_COLLECTION_BASE, &disp);
627             V_DISPATCH(res) = (IDispatch*)disp;
628             break;
629         }
630         default:
631         {
632             FIXME("unimplemented flags %x\n", flags);
633             break;
634         }
635     }
636
637     TRACE("ret %p\n", V_DISPATCH(res));
638
639     return S_OK;
640 }
641
642 static const dispex_static_data_vtbl_t domselection_dispex_vtbl = {
643     domselection_get_dispid,
644     domselection_invoke
645 };
646
647 static const tid_t domselection_iface_tids[] = {
648     IXMLDOMSelection_tid,
649     0
650 };
651 static dispex_static_data_t domselection_dispex = {
652     &domselection_dispex_vtbl,
653     IXMLDOMSelection_tid,
654     NULL,
655     domselection_iface_tids
656 };
657
658 #define XSLPATTERN_CHECK_ARGS(n) \
659     if (nargs != n) { \
660         FIXME("XSLPattern syntax error: Expected %i arguments, got %i\n", n, nargs); \
661         xmlXPathSetArityError(pctx); \
662         return; \
663     }
664
665
666 static void XSLPattern_index(xmlXPathParserContextPtr pctx, int nargs)
667 {
668     XSLPATTERN_CHECK_ARGS(0);
669
670     xmlXPathPositionFunction(pctx, 0);
671     xmlXPathReturnNumber(pctx, xmlXPathPopNumber(pctx) - 1.0);
672 }
673
674 static void XSLPattern_end(xmlXPathParserContextPtr pctx, int nargs)
675 {
676     double pos, last;
677     XSLPATTERN_CHECK_ARGS(0);
678
679     xmlXPathPositionFunction(pctx, 0);
680     pos = xmlXPathPopNumber(pctx);
681     xmlXPathLastFunction(pctx, 0);
682     last = xmlXPathPopNumber(pctx);
683     xmlXPathReturnBoolean(pctx, pos == last);
684 }
685
686 static void XSLPattern_nodeType(xmlXPathParserContextPtr pctx, int nargs)
687 {
688     XSLPATTERN_CHECK_ARGS(0);
689     xmlXPathReturnNumber(pctx, pctx->context->node->type);
690 }
691
692 static void XSLPattern_OP_IEq(xmlXPathParserContextPtr pctx, int nargs)
693 {
694     xmlChar *arg1, *arg2;
695     XSLPATTERN_CHECK_ARGS(2);
696
697     arg2 = xmlXPathPopString(pctx);
698     arg1 = xmlXPathPopString(pctx);
699     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) == 0);
700     xmlFree(arg1);
701     xmlFree(arg2);
702 }
703
704 static void XSLPattern_OP_INEq(xmlXPathParserContextPtr pctx, int nargs)
705 {
706     xmlChar *arg1, *arg2;
707     XSLPATTERN_CHECK_ARGS(2);
708
709     arg2 = xmlXPathPopString(pctx);
710     arg1 = xmlXPathPopString(pctx);
711     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) != 0);
712     xmlFree(arg1);
713     xmlFree(arg2);
714 }
715
716 static void XSLPattern_OP_ILt(xmlXPathParserContextPtr pctx, int nargs)
717 {
718     xmlChar *arg1, *arg2;
719     XSLPATTERN_CHECK_ARGS(2);
720
721     arg2 = xmlXPathPopString(pctx);
722     arg1 = xmlXPathPopString(pctx);
723     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) < 0);
724     xmlFree(arg1);
725     xmlFree(arg2);
726 }
727
728 static void XSLPattern_OP_ILEq(xmlXPathParserContextPtr pctx, int nargs)
729 {
730     xmlChar *arg1, *arg2;
731     XSLPATTERN_CHECK_ARGS(2);
732
733     arg2 = xmlXPathPopString(pctx);
734     arg1 = xmlXPathPopString(pctx);
735     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) <= 0);
736     xmlFree(arg1);
737     xmlFree(arg2);
738 }
739
740 static void XSLPattern_OP_IGt(xmlXPathParserContextPtr pctx, int nargs)
741 {
742     xmlChar *arg1, *arg2;
743     XSLPATTERN_CHECK_ARGS(2);
744
745     arg2 = xmlXPathPopString(pctx);
746     arg1 = xmlXPathPopString(pctx);
747     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) > 0);
748     xmlFree(arg1);
749     xmlFree(arg2);
750 }
751
752 static void XSLPattern_OP_IGEq(xmlXPathParserContextPtr pctx, int nargs)
753 {
754     xmlChar *arg1, *arg2;
755     XSLPATTERN_CHECK_ARGS(2);
756
757     arg2 = xmlXPathPopString(pctx);
758     arg1 = xmlXPathPopString(pctx);
759     xmlXPathReturnBoolean(pctx, xmlStrcasecmp(arg1, arg2) >= 0);
760     xmlFree(arg1);
761     xmlFree(arg2);
762 }
763
764 static void query_serror(void* ctx, xmlErrorPtr err)
765 {
766     LIBXML2_CALLBACK_SERROR(domselection_create, err);
767 }
768
769 HRESULT create_selection(xmlNodePtr node, xmlChar* query, IXMLDOMNodeList **out)
770 {
771     domselection *This = heap_alloc(sizeof(domselection));
772     xmlXPathContextPtr ctxt = xmlXPathNewContext(node->doc);
773     HRESULT hr;
774
775     TRACE("(%p, %s, %p)\n", node, debugstr_a((char const*)query), out);
776
777     *out = NULL;
778     if (!This || !ctxt || !query)
779     {
780         xmlXPathFreeContext(ctxt);
781         heap_free(This);
782         return E_OUTOFMEMORY;
783     }
784
785     This->IXMLDOMSelection_iface.lpVtbl = &domselection_vtbl;
786     This->ref = 1;
787     This->resultPos = 0;
788     This->node = node;
789     This->enumvariant = NULL;
790     init_dispex(&This->dispex, (IUnknown*)&This->IXMLDOMSelection_iface, &domselection_dispex);
791     xmldoc_add_ref(This->node->doc);
792
793     ctxt->error = query_serror;
794     ctxt->node = node;
795     registerNamespaces(ctxt);
796
797     if (is_xpathmode(This->node->doc))
798     {
799         xmlXPathRegisterAllFunctions(ctxt);
800         This->result = xmlXPathEvalExpression(query, ctxt);
801     }
802     else
803     {
804         xmlChar* pattern_query = XSLPattern_to_XPath(ctxt, query);
805
806         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"not", xmlXPathNotFunction);
807         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"boolean", xmlXPathBooleanFunction);
808
809         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"index", XSLPattern_index);
810         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"end", XSLPattern_end);
811         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"nodeType", XSLPattern_nodeType);
812
813         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IEq", XSLPattern_OP_IEq);
814         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_INEq", XSLPattern_OP_INEq);
815         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_ILt", XSLPattern_OP_ILt);
816         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_ILEq", XSLPattern_OP_ILEq);
817         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IGt", XSLPattern_OP_IGt);
818         xmlXPathRegisterFunc(ctxt, (xmlChar const*)"OP_IGEq", XSLPattern_OP_IGEq);
819
820         This->result = xmlXPathEvalExpression(pattern_query, ctxt);
821         xmlFree(pattern_query);
822     }
823
824     if (!This->result || This->result->type != XPATH_NODESET)
825     {
826         hr = E_FAIL;
827         goto cleanup;
828     }
829
830     *out = (IXMLDOMNodeList*)&This->IXMLDOMSelection_iface;
831     hr = S_OK;
832     TRACE("found %d matches\n", xmlXPathNodeSetGetLength(This->result->nodesetval));
833
834 cleanup:
835     if (This && FAILED(hr))
836         IXMLDOMSelection_Release( &This->IXMLDOMSelection_iface );
837     xmlXPathFreeContext(ctxt);
838     return hr;
839 }
840
841 #endif