ddraw/tests: Rewrite LimitTest().
[wine.git] / dlls / wsdapi / xml.c
blob824b7ff2fd15f8260e7ef86372964fdf39936363
1 /*
2 * Web Services on Devices
4 * Copyright 2017 Owen Rudge for CodeWeavers
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.
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.
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
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "wine/debug.h"
28 #include "wine/list.h"
29 #include "wsdapi.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(wsdapi);
33 #define WSD_MAX_TEXT_LENGTH 8192
35 static LPWSTR duplicate_string(void *parentMemoryBlock, LPCWSTR value)
37 int valueLen;
38 LPWSTR dup;
40 valueLen = lstrlenW(value) + 1;
42 dup = WSDAllocateLinkedMemory(parentMemoryBlock, valueLen * sizeof(WCHAR));
44 if (dup) memcpy(dup, value, valueLen * sizeof(WCHAR));
45 return dup;
48 static WSDXML_NAMESPACE *duplicate_namespace(void *parentMemoryBlock, WSDXML_NAMESPACE *ns)
50 WSDXML_NAMESPACE *newNs;
52 newNs = WSDAllocateLinkedMemory(parentMemoryBlock, sizeof(WSDXML_NAMESPACE));
54 if (newNs == NULL)
56 return NULL;
59 newNs->Encoding = ns->Encoding;
61 /* On Windows, both Names and NamesCount are set to null even if there are names present */
62 newNs->NamesCount = 0;
63 newNs->Names = NULL;
65 newNs->PreferredPrefix = duplicate_string(newNs, ns->PreferredPrefix);
66 newNs->Uri = duplicate_string(newNs, ns->Uri);
68 return newNs;
71 static WSDXML_NAME *duplicate_name(void *parentMemoryBlock, WSDXML_NAME *name)
73 WSDXML_NAME *dup;
75 dup = WSDAllocateLinkedMemory(parentMemoryBlock, sizeof(WSDXML_NAME));
77 if (dup == NULL)
79 return NULL;
82 dup->Space = duplicate_namespace(dup, name->Space);
83 dup->LocalName = duplicate_string(dup, name->LocalName);
85 if (dup->LocalName == NULL)
87 WSDFreeLinkedMemory(dup);
88 return NULL;
91 return dup;
94 HRESULT WINAPI WSDXMLAddChild(WSDXML_ELEMENT *pParent, WSDXML_ELEMENT *pChild)
96 WSDXML_NODE *currentNode;
98 TRACE("(%p, %p)\n", pParent, pChild);
100 if ((pParent == NULL) || (pChild == NULL) || (pChild->Node.Parent != NULL))
102 return E_INVALIDARG;
105 /* See if the parent already has a child */
106 currentNode = pParent->FirstChild;
108 if (currentNode == NULL)
110 pParent->FirstChild = (WSDXML_NODE *)pChild;
112 else
114 /* Find the last sibling node and make this child the next sibling */
115 WSDXMLAddSibling((WSDXML_ELEMENT *)currentNode, pChild);
118 pChild->Node.Parent = pParent;
120 /* Link the memory allocations */
121 WSDAttachLinkedMemory(pParent, pChild);
123 return S_OK;
126 HRESULT WINAPI WSDXMLAddSibling(WSDXML_ELEMENT *pFirst, WSDXML_ELEMENT *pSecond)
128 WSDXML_NODE *currentNode;
130 TRACE("(%p, %p)\n", pFirst, pSecond);
132 if ((pFirst == NULL) || (pSecond == NULL))
134 return E_INVALIDARG;
137 /* See if the first node already has a sibling */
138 currentNode = pFirst->Node.Next;
140 if (currentNode == NULL)
142 pFirst->Node.Next = (WSDXML_NODE *)pSecond;
144 else
146 /* Find the last sibling node and make the second element the next sibling */
147 while (1)
149 if (currentNode->Next == NULL)
151 currentNode->Next = (WSDXML_NODE *)pSecond;
152 break;
155 currentNode = currentNode->Next;
159 /* Reparent the second node under the first */
160 pSecond->Node.Parent = pFirst->Node.Parent;
162 /* Link the memory allocations */
163 WSDAttachLinkedMemory(pFirst->Node.Parent, pSecond);
165 return S_OK;
168 HRESULT WINAPI WSDXMLBuildAnyForSingleElement(WSDXML_NAME *pElementName, LPCWSTR pszText, WSDXML_ELEMENT **ppAny)
170 WSDXML_TEXT *child;
172 TRACE("(%p, %s, %p)\n", pElementName, debugstr_w(pszText), ppAny);
174 if ((pElementName == NULL) || ((pszText != NULL) && (lstrlenW(pszText) > WSD_MAX_TEXT_LENGTH)))
176 return E_INVALIDARG;
179 if (ppAny == NULL)
181 return E_POINTER;
184 *ppAny = WSDAllocateLinkedMemory(NULL, sizeof(WSDXML_ELEMENT));
186 if (*ppAny == NULL)
188 return E_OUTOFMEMORY;
191 ZeroMemory(*ppAny, sizeof(WSDXML_ELEMENT));
193 (*ppAny)->Name = duplicate_name(*ppAny, pElementName);
195 if ((*ppAny)->Name == NULL)
197 WSDFreeLinkedMemory(*ppAny);
198 return E_OUTOFMEMORY;
201 if (pszText != NULL)
203 child = WSDAllocateLinkedMemory(*ppAny, sizeof(WSDXML_TEXT));
205 if (child == NULL)
207 WSDFreeLinkedMemory(*ppAny);
208 return E_OUTOFMEMORY;
211 child->Node.Parent = *ppAny;
212 child->Node.Next = NULL;
213 child->Node.Type = TextType;
214 child->Text = duplicate_string(child, pszText);
216 if (child->Text == NULL)
218 WSDFreeLinkedMemory(*ppAny);
219 return E_OUTOFMEMORY;
222 (*ppAny)->FirstChild = (WSDXML_NODE *)child;
225 return S_OK;
228 HRESULT WINAPI WSDXMLCleanupElement(WSDXML_ELEMENT *pAny)
230 TRACE("(%p)\n", pAny);
232 if (pAny == NULL)
234 return E_INVALIDARG;
237 WSDFreeLinkedMemory(pAny);
238 return S_OK;
241 HRESULT WINAPI WSDXMLGetValueFromAny(const WCHAR *pszNamespace, const WCHAR *pszName, WSDXML_ELEMENT *pAny, LPCWSTR *ppszValue)
243 WSDXML_ELEMENT *element;
244 WSDXML_TEXT *text;
246 if (pAny == NULL)
247 return E_INVALIDARG;
249 if (ppszValue == NULL)
250 return E_POINTER;
252 if ((pszNamespace == NULL) || (pszName == NULL) || (lstrlenW(pszNamespace) > WSD_MAX_TEXT_LENGTH) || (lstrlenW(pszName) > WSD_MAX_TEXT_LENGTH))
253 return E_INVALIDARG;
255 element = pAny;
257 while (element != NULL)
259 if (element->Node.Type == ElementType)
261 if ((lstrcmpW(element->Name->LocalName, pszName) == 0) && (lstrcmpW(element->Name->Space->Uri, pszNamespace) == 0))
263 if ((element->FirstChild == NULL) || (element->FirstChild->Type != TextType))
265 return E_FAIL;
268 text = (WSDXML_TEXT *) element->FirstChild;
269 *ppszValue = (LPCWSTR) text->Text;
271 return S_OK;
275 element = (WSDXML_ELEMENT *) element->Node.Next;
278 return E_FAIL;
281 /* IWSDXMLContext implementation */
283 struct xmlNamespace
285 struct list entry;
286 WSDXML_NAMESPACE *namespace;
289 typedef struct IWSDXMLContextImpl
291 IWSDXMLContext IWSDXMLContext_iface;
292 LONG ref;
294 struct list *namespaces;
295 int nextUnknownPrefix;
296 } IWSDXMLContextImpl;
298 static WSDXML_NAMESPACE *find_namespace(struct list *namespaces, LPCWSTR uri)
300 struct xmlNamespace *ns;
302 LIST_FOR_EACH_ENTRY(ns, namespaces, struct xmlNamespace, entry)
304 if (lstrcmpW(ns->namespace->Uri, uri) == 0)
306 return ns->namespace;
310 return NULL;
313 static WSDXML_NAME *find_name(WSDXML_NAMESPACE *ns, LPCWSTR name)
315 int i;
317 for (i = 0; i < ns->NamesCount; i++)
319 if (lstrcmpW(ns->Names[i].LocalName, name) == 0)
321 return &ns->Names[i];
325 return NULL;
328 static WSDXML_NAME *add_name(WSDXML_NAMESPACE *ns, LPCWSTR name)
330 WSDXML_NAME *names;
331 WSDXML_NAME *newName;
332 int i;
334 names = WSDAllocateLinkedMemory(ns, sizeof(WSDXML_NAME) * (ns->NamesCount + 1));
336 if (names == NULL)
338 return NULL;
341 if (ns->NamesCount > 0)
343 /* Copy the existing names array over to the new allocation */
344 memcpy(names, ns->Names, sizeof(WSDXML_NAME) * ns->NamesCount);
346 for (i = 0; i < ns->NamesCount; i++)
348 /* Attach the local name memory to the new names allocation */
349 WSDAttachLinkedMemory(names, names[i].LocalName);
352 WSDFreeLinkedMemory(ns->Names);
355 ns->Names = names;
357 newName = &names[ns->NamesCount];
359 newName->LocalName = duplicate_string(names, name);
360 newName->Space = ns;
362 if (newName->LocalName == NULL)
364 return NULL;
367 ns->NamesCount++;
368 return newName;
371 static BOOL is_prefix_unique(struct list *namespaces, LPCWSTR prefix)
373 struct xmlNamespace *ns;
375 LIST_FOR_EACH_ENTRY(ns, namespaces, struct xmlNamespace, entry)
377 if (lstrcmpW(ns->namespace->PreferredPrefix, prefix) == 0)
379 return FALSE;
383 return TRUE;
386 static LPWSTR generate_namespace_prefix(IWSDXMLContextImpl *impl, void *parentMemoryBlock, LPCWSTR uri)
388 WCHAR formatString[] = { 'u','n','%','d', 0 };
389 WCHAR suggestedPrefix[7];
391 /* Find a unique prefix */
392 while (impl->nextUnknownPrefix < 1000)
394 wsprintfW(suggestedPrefix, formatString, impl->nextUnknownPrefix++);
396 /* For the unlikely event where somebody has explicitly created a prefix called 'unX', check it is unique */
397 if (is_prefix_unique(impl->namespaces, suggestedPrefix))
399 return duplicate_string(parentMemoryBlock, suggestedPrefix);
403 return NULL;
406 static WSDXML_NAMESPACE *add_namespace(struct list *namespaces, LPCWSTR uri)
408 struct xmlNamespace *ns = WSDAllocateLinkedMemory(namespaces, sizeof(struct xmlNamespace));
410 if (ns == NULL)
412 return NULL;
415 ns->namespace = WSDAllocateLinkedMemory(ns, sizeof(WSDXML_NAMESPACE));
417 if (ns->namespace == NULL)
419 WSDFreeLinkedMemory(ns);
420 return NULL;
423 ZeroMemory(ns->namespace, sizeof(WSDXML_NAMESPACE));
424 ns->namespace->Uri = duplicate_string(ns->namespace, uri);
426 if (ns->namespace->Uri == NULL)
428 WSDFreeLinkedMemory(ns);
429 return NULL;
432 list_add_tail(namespaces, &ns->entry);
433 return ns->namespace;
436 static inline IWSDXMLContextImpl *impl_from_IWSDXMLContext(IWSDXMLContext *iface)
438 return CONTAINING_RECORD(iface, IWSDXMLContextImpl, IWSDXMLContext_iface);
441 static HRESULT WINAPI IWSDXMLContextImpl_QueryInterface(IWSDXMLContext *iface, REFIID riid, void **ppv)
443 IWSDXMLContextImpl *This = impl_from_IWSDXMLContext(iface);
445 TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppv);
447 if (!ppv)
449 WARN("Invalid parameter\n");
450 return E_INVALIDARG;
453 *ppv = NULL;
455 if (IsEqualIID(riid, &IID_IUnknown) ||
456 IsEqualIID(riid, &IID_IWSDXMLContext))
458 *ppv = &This->IWSDXMLContext_iface;
460 else
462 WARN("Unknown IID %s\n", debugstr_guid(riid));
463 return E_NOINTERFACE;
466 IUnknown_AddRef((IUnknown*)*ppv);
467 return S_OK;
470 static ULONG WINAPI IWSDXMLContextImpl_AddRef(IWSDXMLContext *iface)
472 IWSDXMLContextImpl *This = impl_from_IWSDXMLContext(iface);
473 ULONG ref = InterlockedIncrement(&This->ref);
475 TRACE("(%p) ref=%d\n", This, ref);
476 return ref;
479 static ULONG WINAPI IWSDXMLContextImpl_Release(IWSDXMLContext *iface)
481 IWSDXMLContextImpl *This = impl_from_IWSDXMLContext(iface);
482 ULONG ref = InterlockedDecrement(&This->ref);
484 TRACE("(%p) ref=%d\n", This, ref);
486 if (ref == 0)
488 WSDFreeLinkedMemory(This);
491 return ref;
494 static HRESULT WINAPI IWSDXMLContextImpl_AddNamespace(IWSDXMLContext *iface, LPCWSTR pszUri, LPCWSTR pszSuggestedPrefix, WSDXML_NAMESPACE **ppNamespace)
496 IWSDXMLContextImpl *This = impl_from_IWSDXMLContext(iface);
497 WSDXML_NAMESPACE *ns;
498 LPCWSTR newPrefix = NULL;
499 BOOL setNewPrefix;
501 TRACE("(%p, %s, %s, %p)\n", This, debugstr_w(pszUri), debugstr_w(pszSuggestedPrefix), ppNamespace);
503 if ((pszUri == NULL) || (pszSuggestedPrefix == NULL) || (lstrlenW(pszUri) > WSD_MAX_TEXT_LENGTH) ||
504 (lstrlenW(pszSuggestedPrefix) > WSD_MAX_TEXT_LENGTH))
506 return E_INVALIDARG;
509 ns = find_namespace(This->namespaces, pszUri);
511 if (ns == NULL)
513 ns = add_namespace(This->namespaces, pszUri);
515 if (ns == NULL)
517 return E_OUTOFMEMORY;
521 setNewPrefix = (ns->PreferredPrefix == NULL);
523 if ((ns->PreferredPrefix == NULL) || (lstrcmpW(ns->PreferredPrefix, pszSuggestedPrefix) != 0))
525 newPrefix = pszSuggestedPrefix;
526 setNewPrefix = TRUE;
529 if (setNewPrefix)
531 WSDFreeLinkedMemory((void *)ns->PreferredPrefix);
532 ns->PreferredPrefix = NULL;
534 if ((newPrefix != NULL) && (is_prefix_unique(This->namespaces, newPrefix)))
536 ns->PreferredPrefix = duplicate_string(ns, newPrefix);
538 else
540 ns->PreferredPrefix = generate_namespace_prefix(This, ns, pszUri);
541 if (ns->PreferredPrefix == NULL)
543 return E_FAIL;
548 if (ppNamespace != NULL)
550 *ppNamespace = duplicate_namespace(NULL, ns);
552 if (*ppNamespace == NULL)
554 return E_OUTOFMEMORY;
558 return S_OK;
561 static HRESULT WINAPI IWSDXMLContextImpl_AddNameToNamespace(IWSDXMLContext *iface, LPCWSTR pszUri, LPCWSTR pszName, WSDXML_NAME **ppName)
563 IWSDXMLContextImpl *This = impl_from_IWSDXMLContext(iface);
564 WSDXML_NAMESPACE *ns;
565 WSDXML_NAME *name;
567 TRACE("(%p, %s, %s, %p)\n", This, debugstr_w(pszUri), debugstr_w(pszName), ppName);
569 if ((pszUri == NULL) || (pszName == NULL) || (lstrlenW(pszUri) > WSD_MAX_TEXT_LENGTH) || (lstrlenW(pszName) > WSD_MAX_TEXT_LENGTH))
571 return E_INVALIDARG;
574 ns = find_namespace(This->namespaces, pszUri);
576 if (ns == NULL)
578 /* The namespace doesn't exist, add it */
579 ns = add_namespace(This->namespaces, pszUri);
581 if (ns == NULL)
583 return E_OUTOFMEMORY;
586 ns->PreferredPrefix = generate_namespace_prefix(This, ns, pszUri);
587 if (ns->PreferredPrefix == NULL)
589 return E_FAIL;
593 name = find_name(ns, pszName);
595 if (name == NULL)
597 name = add_name(ns, pszName);
599 if (name == NULL)
601 return E_OUTOFMEMORY;
605 if (ppName != NULL)
607 *ppName = duplicate_name(NULL, name);
609 if (*ppName == NULL)
611 return E_OUTOFMEMORY;
615 return S_OK;
618 static HRESULT WINAPI IWSDXMLContextImpl_SetNamespaces(IWSDXMLContext *iface, const PCWSDXML_NAMESPACE *pNamespaces, WORD wNamespacesCount, BYTE bLayerNumber)
620 FIXME("(%p, %p, %d, %d)\n", iface, pNamespaces, wNamespacesCount, bLayerNumber);
621 return E_NOTIMPL;
624 static HRESULT WINAPI IWSDXMLContextImpl_SetTypes(IWSDXMLContext *iface, const PCWSDXML_TYPE *pTypes, DWORD dwTypesCount, BYTE bLayerNumber)
626 FIXME("(%p, %p, %d, %d)\n", iface, pTypes, dwTypesCount, bLayerNumber);
627 return E_NOTIMPL;
630 static const IWSDXMLContextVtbl xmlcontext_vtbl =
632 IWSDXMLContextImpl_QueryInterface,
633 IWSDXMLContextImpl_AddRef,
634 IWSDXMLContextImpl_Release,
635 IWSDXMLContextImpl_AddNamespace,
636 IWSDXMLContextImpl_AddNameToNamespace,
637 IWSDXMLContextImpl_SetNamespaces,
638 IWSDXMLContextImpl_SetTypes
641 HRESULT WINAPI WSDXMLCreateContext(IWSDXMLContext **ppContext)
643 IWSDXMLContextImpl *obj;
645 TRACE("(%p)\n", ppContext);
647 if (ppContext == NULL)
649 WARN("Invalid parameter: ppContext == NULL\n");
650 return E_POINTER;
653 *ppContext = NULL;
655 obj = WSDAllocateLinkedMemory(NULL, sizeof(*obj));
657 if (!obj)
659 return E_OUTOFMEMORY;
662 obj->IWSDXMLContext_iface.lpVtbl = &xmlcontext_vtbl;
663 obj->ref = 1;
664 obj->namespaces = WSDAllocateLinkedMemory(obj, sizeof(struct list));
665 obj->nextUnknownPrefix = 0;
667 if (obj->namespaces == NULL)
669 WSDFreeLinkedMemory(obj);
670 return E_OUTOFMEMORY;
673 list_init(obj->namespaces);
675 *ppContext = &obj->IWSDXMLContext_iface;
676 TRACE("Returning iface %p\n", *ppContext);
678 return S_OK;