summaryrefslogtreecommitdiffstats
path: root/storage/connect/domdoc.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'storage/connect/domdoc.cpp')
-rw-r--r--storage/connect/domdoc.cpp755
1 files changed, 755 insertions, 0 deletions
diff --git a/storage/connect/domdoc.cpp b/storage/connect/domdoc.cpp
new file mode 100644
index 00000000..268ad771
--- /dev/null
+++ b/storage/connect/domdoc.cpp
@@ -0,0 +1,755 @@
+/******************************************************************/
+/* Implementation of XML document processing using MS DOM */
+/* Author: Olivier Bertrand 2007 - 2013 */
+/******************************************************************/
+#include "my_global.h"
+#include <stdio.h>
+#if defined(_WIN32)
+//#include <windows.h>
+#if defined(MSX2)
+#import "msxml2.dll" //Does not exist on Vista
+#elif defined(MSX3)
+#import "msxml3.dll" //Causes error C2872: DOMNodeType: ambiguous symbol ??
+#elif defined(MSX4)
+#import "msxml4.dll" //Causes error C2872: DOMNodeType: ambiguous symbol ??
+#elif defined(MSX6)
+#pragma warning(suppress : 4192)
+#import "msxml6.dll" //Causes error C2872: DOMNodeType: ambiguous symbol ??
+#else // MSX4
+#error MSX? is not defined
+#endif // MSX
+using namespace MSXML2;
+#else
+#error This is a Windows implementation only
+#endif
+
+#define NODE_TYPE_LIST
+
+#include "global.h"
+#include "plgdbsem.h"
+#include "xobject.h"
+#include "domdoc.h"
+
+inline bool TestHr(PGLOBAL g, HRESULT hr)
+ {
+ if FAILED(hr) {
+ snprintf(g->Message, sizeof(g->Message), "%s, hr=%d", MSG(COM_ERROR), hr);
+ return true;
+ } else
+ return false;
+
+ } // end of TestHr
+
+/******************************************************************/
+/* Return a DOMDOC as a XMLDOC. */
+/******************************************************************/
+PXDOC GetDomDoc(PGLOBAL g, char *nsl, char *nsdf,
+ char *enc, PFBLOCK fp)
+ {
+ return (PXDOC) new(g) DOMDOC(nsl, nsdf, enc, fp);
+ } // end of GetDomDoc
+
+/***********************************************************************/
+/* Close a loaded DOM XML file. */
+/***********************************************************************/
+void CloseXMLFile(PGLOBAL g, PFBLOCK fp, bool all)
+ {
+ PXBLOCK xp = (PXBLOCK)fp;
+
+ if (xp && xp->Count > 1 && !all) {
+ xp->Count--;
+ } else if (xp && xp->Count > 0) {
+ try {
+ if (xp->Docp)
+ xp->Docp->Release();
+
+ } catch(_com_error e) {
+ char *p = _com_util::ConvertBSTRToString(e.Description());
+ snprintf(g->Message, sizeof(g->Message), "%s %s", MSG(COM_ERROR), p);
+ delete[] p;
+ } catch(...) {}
+
+ CoUninitialize();
+ xp->Count = 0;
+ } // endif
+
+ } // end of CloseXMLFile
+
+/* ------------------------ class DOMDOC ------------------------ */
+
+/******************************************************************/
+/* DOMDOC constructor. */
+/******************************************************************/
+DOMDOC::DOMDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp)
+ : XMLDOCUMENT(nsl, nsdf, enc)
+ {
+ assert (!fp || fp->Type == TYPE_FB_XML);
+ Docp = (fp) ? ((PXBLOCK)fp)->Docp : (MSXML2::IXMLDOMDocumentPtr)NULL;
+ Nlist = NULL;
+ Hr = 0;
+ } // end of DOMDOC constructor
+
+/******************************************************************/
+/* Initialize XML parser and check library compatibility. */
+/******************************************************************/
+bool DOMDOC::Initialize(PGLOBAL g, PCSZ entry, bool zipped)
+{
+ if (zipped && InitZip(g, entry))
+ return true;
+
+ if (TestHr(g, CoInitialize(NULL)))
+ return true;
+
+ if (TestHr(g, Docp.CreateInstance("msxml2.domdocument")))
+ return true;
+
+ return MakeNSlist(g);
+} // end of Initialize
+
+/******************************************************************/
+/* Parse the XML file and construct node tree in memory. */
+/******************************************************************/
+bool DOMDOC::ParseFile(PGLOBAL g, char *fn)
+{
+ bool b;
+
+ Docp->async = false;
+
+ if (zip) {
+ // Parse an in memory document
+ char *xdoc = GetMemDoc(g, fn);
+
+ // This is not equivalent to load for UTF8 characters
+ // It is why get node content is not the same
+ b = (xdoc) ? (bool)Docp->loadXML((_bstr_t)xdoc) : false;
+ } else
+ // Load the document
+ b = (bool)Docp->load((_bstr_t)fn);
+
+ if (!b)
+ return true;
+
+ return false;
+} // end of ParseFile
+
+/******************************************************************/
+/* Create or reuse an Xblock for this document. */
+/******************************************************************/
+PFBLOCK DOMDOC::LinkXblock(PGLOBAL g, MODE m, int rc, char *fn)
+ {
+ PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
+ PXBLOCK xp = (PXBLOCK)PlugSubAlloc(g, NULL, sizeof(XBLOCK));
+
+ memset(xp, 0, sizeof(XBLOCK));
+ xp->Next = (PXBLOCK)dup->Openlist;
+ dup->Openlist = (PFBLOCK)xp;
+ xp->Type = TYPE_FB_XML;
+ xp->Fname = (LPCSTR)PlugSubAlloc(g, NULL, strlen(fn) + 1);
+ strcpy((char*)xp->Fname, fn);
+ xp->Count = 1;
+ xp->Length = (m == MODE_READ) ? 1 : 0;
+ xp->Docp = Docp;
+ xp->Retcode = rc;
+
+ // Return xp as a fp
+ return (PFBLOCK)xp;
+ } // end of LinkXblock
+
+/******************************************************************/
+/* Create the XML node. */
+/******************************************************************/
+bool DOMDOC::NewDoc(PGLOBAL g, PCSZ ver)
+ {
+ char buf[64];
+ MSXML2::IXMLDOMProcessingInstructionPtr pip;
+
+ sprintf(buf, "version=\"%s\" encoding=\"%s\"", ver, Encoding);
+ pip = Docp->createProcessingInstruction("xml", buf);
+ return(TestHr(g, Docp->appendChild(pip)));
+ } // end of NewDoc
+
+/******************************************************************/
+/* Add a comment to the document node. */
+/******************************************************************/
+void DOMDOC::AddComment(PGLOBAL g, char *com)
+ {
+ TestHr(g, Docp->appendChild(Docp->createComment(com)));
+ } // end of AddComment
+
+/******************************************************************/
+/* Return the node class of the root of the document. */
+/******************************************************************/
+PXNODE DOMDOC::GetRoot(PGLOBAL g)
+ {
+ MSXML2::IXMLDOMElementPtr root = Docp->documentElement;
+
+ if (root == NULL)
+ return NULL;
+
+ return new(g) DOMNODE(this, root);
+ } // end of GetRoot
+
+/******************************************************************/
+/* Create a new root element and return its class node. */
+/******************************************************************/
+PXNODE DOMDOC::NewRoot(PGLOBAL g, char *name)
+ {
+ MSXML2::IXMLDOMElementPtr ep = Docp->createElement(name);
+
+ if (ep == NULL || TestHr(g, Docp->appendChild(ep)))
+ return NULL;
+
+ return new(g) DOMNODE(this, ep);
+ } // end of NewRoot
+
+/******************************************************************/
+/* Return a void DOMNODE node class. */
+/******************************************************************/
+PXNODE DOMDOC::NewPnode(PGLOBAL g, char *name)
+ {
+ MSXML2::IXMLDOMElementPtr root = NULL;
+
+ if (name)
+ if ((root = Docp->createElement(name)) == NULL)
+ return NULL;
+
+ return new(g) DOMNODE(this, root);
+ } // end of NewPnode
+
+/******************************************************************/
+/* Return a void DOMATTR node class. */
+/******************************************************************/
+PXATTR DOMDOC::NewPattr(PGLOBAL g)
+ {
+ return new(g) DOMATTR(this, NULL);
+ } // end of NewPattr
+
+/******************************************************************/
+/* Return a void DOMATTR node class. */
+/******************************************************************/
+PXLIST DOMDOC::NewPlist(PGLOBAL g)
+ {
+ return new(g) DOMNODELIST(this, NULL);
+ } // end of NewPlist
+
+/******************************************************************/
+/* Dump the node tree to a new XML file. */
+/******************************************************************/
+int DOMDOC::DumpDoc(PGLOBAL g, char *ofn)
+ {
+ int rc = 0;
+
+ try {
+ Docp->save(ofn);
+ } catch(_com_error e) {
+ snprintf(g->Message, sizeof(g->Message), "%s: %s", MSG(COM_ERROR),
+ _com_util::ConvertBSTRToString(e.Description()));
+ rc = -1;
+ } catch(...) {}
+
+ return rc;
+ } // end of Dump
+
+/******************************************************************/
+/* Free the document, cleanup the XML library, and */
+/* debug memory for regression tests. */
+/******************************************************************/
+void DOMDOC::CloseDoc(PGLOBAL g, PFBLOCK xp)
+ {
+ CloseXMLFile(g, xp, false);
+ CloseZip();
+ } // end of Close
+
+/* ----------------------- class DOMNODE ------------------------ */
+
+/******************************************************************/
+/* DOMNODE constructor. */
+/******************************************************************/
+DOMNODE::DOMNODE(PXDOC dp, MSXML2::IXMLDOMNodePtr np) : XMLNODE(dp)
+ {
+ Docp = ((PDOMDOC)dp)->Docp;
+ Nodep = np;
+ Ws = NULL;
+ Len = 0;
+ Zip = (bool)dp->zip;
+ } // end of DOMNODE constructor
+
+/******************************************************************/
+/* Return the node name. */
+/******************************************************************/
+char *DOMNODE::GetName(PGLOBAL g)
+ {
+ if (!WideCharToMultiByte(CP_ACP, 0, Nodep->nodeName, -1,
+ Name, sizeof(Name), NULL, NULL)) {
+ strcpy(g->Message, MSG(NAME_CONV_ERR));
+ return NULL;
+ } // endif
+
+ return Name;
+ } // end of GetName
+
+/******************************************************************/
+/* Return the node class of next sibling of the node. */
+/******************************************************************/
+PXNODE DOMNODE::GetNext(PGLOBAL g)
+ {
+ if (Nodep->nextSibling == NULL)
+ Next = NULL;
+ else // if (!Next)
+ Next = new(g) DOMNODE(Doc, Nodep->nextSibling);
+
+ return Next;
+ } // end of GetNext
+
+/******************************************************************/
+/* Return the node class of first children of the node. */
+/******************************************************************/
+PXNODE DOMNODE::GetChild(PGLOBAL g)
+ {
+ if (Nodep->firstChild == NULL)
+ Children = NULL;
+ else // if (!Children)
+ Children = new(g) DOMNODE(Doc, Nodep->firstChild);
+
+ return Children;
+ } // end of GetChild
+
+/******************************************************************/
+/* Return the content of a node and subnodes. */
+/******************************************************************/
+RCODE DOMNODE::GetContent(PGLOBAL g, char *buf, int len)
+ {
+ RCODE rc = RC_OK;
+
+ // Nodep can be null for a missing HTML table column
+ if (Nodep) {
+ if (Zip) {
+ strcpy(buf, Nodep->text);
+ } else if (!WideCharToMultiByte(CP_UTF8, 0, Nodep->text, -1,
+ buf, len, NULL, NULL)) {
+ DWORD lsr = GetLastError();
+
+ switch (lsr) {
+ case 0:
+ case ERROR_INSUFFICIENT_BUFFER: // 122L
+ snprintf(g->Message, sizeof(g->Message), "Truncated %s content", GetName(g));
+ rc = RC_INFO;
+ break;
+ case ERROR_NO_UNICODE_TRANSLATION: // 1113L
+ snprintf(g->Message, sizeof(g->Message), "Invalid character(s) in %s content",
+ GetName(g));
+ rc = RC_INFO;
+ break;
+ default:
+ snprintf(g->Message, sizeof(g->Message), "System error getting %s content",
+ GetName(g));
+ rc = RC_FX;
+ break;
+ } // endswitch
+
+ } // endif
+
+ } else
+ *buf = '\0';
+
+ return rc;
+ } // end of GetContent
+
+/******************************************************************/
+/* Set the text content of an attribute. */
+/******************************************************************/
+bool DOMNODE::SetContent(PGLOBAL g, char *txtp, int len)
+ {
+ bool rc;
+ BSTR val;
+
+ if (len > Len || !Ws) {
+ Ws = (WCHAR*)PlugSubAlloc(g, NULL, (len + 1) * 2);
+ Len = len;
+ } // endif len
+
+ if (!MultiByteToWideChar(CP_UTF8, 0, txtp, strlen(txtp) + 1,
+ Ws, Len + 1)) {
+ snprintf(g->Message, sizeof(g->Message), MSG(WS_CONV_ERR), txtp);
+ return true;
+ } // endif
+
+ val = SysAllocString(Ws);
+ rc = TestHr(g, Nodep->put_text(val));
+ SysFreeString(val);
+ return rc;
+ } // end of SetContent
+
+/******************************************************************/
+/* Return a clone of this node. */
+/******************************************************************/
+PXNODE DOMNODE::Clone(PGLOBAL g, PXNODE np)
+ {
+ if (np) {
+ ((PDOMNODE)np)->Nodep = Nodep;
+ return np;
+ } else
+ return new(g) DOMNODE(Doc, Nodep);
+
+ } // end of Clone
+
+/******************************************************************/
+/* Return the list of all or matching children that are elements.*/
+/******************************************************************/
+PXLIST DOMNODE::GetChildElements(PGLOBAL g, char *xp, PXLIST lp)
+ {
+ MSXML2::IXMLDOMNodeListPtr dnlp;
+
+ if (xp) {
+ if (Nodep->nodeType == MSXML2::NODE_ELEMENT) {
+ MSXML2::IXMLDOMElementPtr ep = Nodep;
+ dnlp = ep->getElementsByTagName(xp);
+ } else
+ return NULL;
+
+ } else
+ dnlp = Nodep->childNodes;
+
+ if (lp) {
+ ((PDOMLIST)lp)->Listp = dnlp;
+ return lp;
+ } else
+ return new(g) DOMNODELIST(Doc, dnlp);
+
+ } // end of GetChildElements
+
+/******************************************************************/
+/* Return the list of nodes verifying the passed Xapth. */
+/******************************************************************/
+PXLIST DOMNODE::SelectNodes(PGLOBAL g, char *xp, PXLIST lp)
+ {
+ MSXML2::IXMLDOMNodeListPtr dnlp = Nodep->selectNodes(xp);
+
+ if (lp) {
+ ((PDOMLIST)lp)->Listp = dnlp;
+ return lp;
+ } else
+ return new(g) DOMNODELIST(Doc, dnlp);
+
+ } // end of SelectNodes
+
+/******************************************************************/
+/* Return the first node verifying the passed Xapth. */
+/******************************************************************/
+PXNODE DOMNODE::SelectSingleNode(PGLOBAL g, char *xp, PXNODE np)
+ {
+ try {
+ MSXML2::IXMLDOMNodePtr dnp = Nodep->selectSingleNode(xp);
+
+ if (dnp) {
+ if (np) {
+ ((PDOMNODE)np)->Nodep = dnp;
+ return np;
+ } else
+ return new(g) DOMNODE(Doc, dnp);
+
+ } // endif dnp
+
+ } catch(_com_error e) {
+ snprintf(g->Message, sizeof(g->Message), "%s: %s", MSG(COM_ERROR),
+ _com_util::ConvertBSTRToString(e.Description()));
+ } catch(...) {}
+
+ return NULL;
+ } // end of SelectSingleNode
+
+/******************************************************************/
+/* Return the node attribute with the specified name. */
+/******************************************************************/
+PXATTR DOMNODE::GetAttribute(PGLOBAL g, char *name, PXATTR ap)
+ {
+ MSXML2::IXMLDOMElementPtr ep;
+ MSXML2::IXMLDOMNamedNodeMapPtr nmp;
+ MSXML2::IXMLDOMAttributePtr atp;
+
+ if (name) {
+ ep = Nodep;
+ atp = ep->getAttributeNode(name);
+ nmp = NULL;
+ } else {
+ nmp = Nodep->Getattributes();
+ atp = nmp->Getitem(0);
+ } // endif name
+
+ if (atp) {
+ if (ap) {
+ ((PDOMATTR)ap)->Atrp = atp;
+ ((PDOMATTR)ap)->Nmp = nmp;
+ ((PDOMATTR)ap)->K = 0;
+ return ap;
+ } else
+ return new(g) DOMATTR(Doc, atp, nmp);
+
+ } else
+ return NULL;
+
+ } // end of GetAttribute
+
+/******************************************************************/
+/* Add a new element child node to this node and return it. */
+/******************************************************************/
+PXNODE DOMNODE::AddChildNode(PGLOBAL g, PCSZ name, PXNODE np)
+ {
+ const char *p, *pn;
+// char *p, *pn, *epf, *pf = NULL;
+ MSXML2::IXMLDOMNodePtr ep;
+// _bstr_t uri((wchar_t*)NULL);
+
+#if 0
+ // Is a prefix specified ?
+ if ((p = strchr(name, ':'))) {
+ pf = BufAlloc(g, name, p - name);
+
+ // Is it the pseudo default prefix
+ if (Doc->DefNs && !strcmp(pf, Doc->DefNs)) {
+ name = p + 1; // Suppress it from name
+ pf = NULL; // No real prefix
+ } // endif DefNs
+
+ } // endif p
+
+ // Look for matching namespace URI in context
+ for (ep = Nodep; ep; ep = ep->parentNode) {
+ epf = (_bstr_t)ep->prefix;
+
+ if ((!pf && !epf) || (pf && epf && !strcmp(pf, epf))) {
+ uri = Nodep->namespaceURI;
+ break;
+ } // endif
+
+ } // endfor ep
+
+ if ((wchar_t*)uri == NULL) {
+ if (!pf)
+ pf = Doc->DefNs;
+
+ // Look for the namespace URI corresponding to this node
+ if (pf)
+ for (PNS nsp = Doc->Namespaces; nsp; nsp = nsp->Next)
+ if (!strcmp(pf, nsp->Prefix)) {
+ uri = nsp->Uri;
+ break;
+ } // endfor nsp
+
+ } // endif pns
+#endif // 0
+
+ // If name has the format m[n] only m is taken as node name
+ if ((p = strchr(name, '[')))
+ pn = BufAlloc(g, name, (int)(p - name));
+ else
+ pn = name;
+
+ // Construct the element node with eventual namespace
+// ep = Docp->createNode(_variant_t("Element"), pn, uri);
+ ep = Docp->createElement(pn);
+
+ _bstr_t pfx = ep->prefix;
+ _bstr_t uri = ep->namespaceURI;
+
+ if (ep == NULL || TestHr(g, Nodep->appendChild(ep)))
+ return NULL;
+
+ if (np)
+ ((PDOMNODE)np)->Nodep = ep;
+ else
+ np = new(g) DOMNODE(Doc, ep);
+
+ return NewChild(np);
+ } // end of AddChildNode
+
+/******************************************************************/
+/* Add a new property to this node and return it. */
+/******************************************************************/
+PXATTR DOMNODE::AddProperty(PGLOBAL g, char *name, PXATTR ap)
+ {
+ MSXML2::IXMLDOMAttributePtr atp = Docp->createAttribute(name);
+
+ if (atp) {
+ MSXML2::IXMLDOMElementPtr ep = Nodep;
+ ep->setAttributeNode(atp);
+
+ if (ap) {
+ ((PDOMATTR)ap)->Atrp = atp;
+ return ap;
+ } else
+ return new(g) DOMATTR(Doc, atp);
+
+ } else
+ return NULL;
+
+ } // end of AddProperty
+
+/******************************************************************/
+/* Add a new text node to this node. */
+/******************************************************************/
+void DOMNODE::AddText(PGLOBAL g, PCSZ txtp)
+ {
+ MSXML2::IXMLDOMTextPtr tp= Docp->createTextNode((_bstr_t)txtp);
+
+ if (tp != NULL)
+ TestHr(g, Nodep->appendChild(tp));
+
+ } // end of AddText
+
+/******************************************************************/
+/* Remove a child node from this node. */
+/******************************************************************/
+void DOMNODE::DeleteChild(PGLOBAL g, PXNODE dnp)
+ {
+ TestHr(g, Nodep->removeChild(((PDOMNODE)dnp)->Nodep));
+// ((PDOMNODE)dnp)->Nodep->Release(); bad idea, causes a crash
+ Delete(dnp);
+ } // end of DeleteChild
+
+/* --------------------- class DOMNODELIST ---------------------- */
+
+/******************************************************************/
+/* DOMNODELIST constructor. */
+/******************************************************************/
+DOMNODELIST::DOMNODELIST(PXDOC dp, MSXML2::IXMLDOMNodeListPtr lp)
+ : XMLNODELIST(dp)
+ {
+ Listp = lp;
+ } // end of DOMNODELIST constructor
+
+/******************************************************************/
+/* Return the nth element of the list. */
+/******************************************************************/
+PXNODE DOMNODELIST::GetItem(PGLOBAL g, int n, PXNODE np)
+ {
+ if (Listp == NULL || Listp->length <= n)
+ return NULL;
+
+ if (np) {
+ ((PDOMNODE)np)->Nodep = Listp->item[n];
+ return np;
+ } else
+ return new(g) DOMNODE(Doc, Listp->item[n]);
+
+ } // end of GetItem
+
+/******************************************************************/
+/* Reset the pointer on the deleted item. */
+/******************************************************************/
+bool DOMNODELIST::DropItem(PGLOBAL g, int n)
+{
+ if (Listp == NULL || Listp->length < n)
+ return true;
+
+ return false;
+} // end of DeleteItem
+
+/* ----------------------- class DOMATTR ------------------------ */
+
+/******************************************************************/
+/* DOMATTR constructor. */
+/******************************************************************/
+DOMATTR::DOMATTR(PXDOC dp, MSXML2::IXMLDOMAttributePtr ap,
+ MSXML2::IXMLDOMNamedNodeMapPtr nmp)
+ : XMLATTRIBUTE(dp)
+ {
+ Atrp = ap;
+ Nmp = nmp;
+ Ws = NULL;
+ Len = 0;
+ K = 0;
+ } // end of DOMATTR constructor
+
+/******************************************************************/
+/* Return the attribute name. */
+/******************************************************************/
+char *DOMATTR::GetName(PGLOBAL g)
+ {
+ if (!WideCharToMultiByte(CP_ACP, 0, Atrp->nodeName, -1,
+ Name, sizeof(Name), NULL, NULL)) {
+ strcpy(g->Message, MSG(NAME_CONV_ERR));
+ return NULL;
+ } // endif
+
+ return Name;
+ } // end of GetName
+
+/******************************************************************/
+/* Return the next attribute node. */
+/* This funtion is implemented as needed by XMLColumns. */
+/******************************************************************/
+PXATTR DOMATTR::GetNext(PGLOBAL g)
+ {
+ if (!Nmp)
+ return NULL;
+
+ if (++K >= Nmp->Getlength()) {
+ Nmp->reset();
+ Nmp = NULL;
+ K = 0;
+ return NULL;
+ } // endif K
+
+ Atrp = Nmp->Getitem(K);
+ return this;
+ } // end of GetNext
+
+/******************************************************************/
+/* Return the content of a node and subnodes. */
+/******************************************************************/
+RCODE DOMATTR::GetText(PGLOBAL g, char *buf, int len)
+ {
+ RCODE rc = RC_OK;
+
+ if (!WideCharToMultiByte(CP_UTF8, 0, Atrp->text, -1,
+ buf, len, NULL, NULL)) {
+ DWORD lsr = GetLastError();
+
+ switch (lsr) {
+ case 0:
+ case ERROR_INSUFFICIENT_BUFFER: // 122L
+ snprintf(g->Message, sizeof(g->Message), "Truncated %s content", GetName(g));
+ rc = RC_INFO;
+ break;
+ case ERROR_NO_UNICODE_TRANSLATION: // 1113L
+ snprintf(g->Message, sizeof(g->Message), "Invalid character(s) in %s content",
+ GetName(g));
+ rc = RC_INFO;
+ break;
+ default:
+ snprintf(g->Message, sizeof(g->Message), "System error getting %s content",
+ GetName(g));
+ rc = RC_FX;
+ break;
+ } // endswitch
+
+ } // endif
+
+ return rc;
+ } // end of GetText
+
+/******************************************************************/
+/* Set the text content of an attribute. */
+/******************************************************************/
+bool DOMATTR::SetText(PGLOBAL g, char *txtp, int len)
+ {
+ bool rc;
+ BSTR val;
+
+ if (len > Len || !Ws) {
+ Ws = (WCHAR*)PlugSubAlloc(g, NULL, (len + 1) * 2);
+ Len = len;
+ } // endif len
+
+ if (!MultiByteToWideChar(CP_UTF8, 0, txtp, strlen(txtp) + 1,
+ Ws, Len + 1)) {
+ snprintf(g->Message, sizeof(g->Message), MSG(WS_CONV_ERR), txtp);
+ return true;
+ } // endif
+
+ val = SysAllocString(Ws);
+ rc = TestHr(g, Atrp->put_text(val));
+ SysFreeString(val);
+ return rc;
+ } // end of SetText