1 /******************************************************************/
2 /*  Implementation of XML document processing using MS DOM        */
3 /*  Author: Olivier Bertrand                2007 - 2013           */
4 /******************************************************************/
5 #include "my_global.h"
6 #include <stdio.h>
7 #if defined(_WIN32)
8 //#include <windows.h>
9 #if   defined(MSX2)
10 #import "msxml2.dll"  //Does not exist on Vista
11 #elif defined(MSX3)
12 #import "msxml3.dll"  //Causes error C2872: DOMNodeType: ambiguous symbol  ??
13 #elif defined(MSX4)
14 #import "msxml4.dll"  //Causes error C2872: DOMNodeType: ambiguous symbol  ??
15 #elif defined(MSX6)
16 #pragma warning(suppress : 4192)
17 #import "msxml6.dll"  //Causes error C2872: DOMNodeType: ambiguous symbol  ??
18 #else       // MSX4
19 #error MSX? is not defined
20 #endif     // MSX
21 using namespace MSXML2;
22 #else
23 #error This is a Windows implementation only
24 #endif
25 
26 #define NODE_TYPE_LIST
27 
28 #include "global.h"
29 #include "plgdbsem.h"
30 #include "xobject.h"
31 #include "domdoc.h"
32 
TestHr(PGLOBAL g,HRESULT hr)33 inline bool TestHr(PGLOBAL g, HRESULT hr)
34   {
35   if FAILED(hr) {
36     sprintf(g->Message, "%s, hr=%d", MSG(COM_ERROR), hr);
37     return true;
38   } else
39     return false;
40 
41   } // end of TestHr
42 
43 /******************************************************************/
44 /*  Return a DOMDOC as a XMLDOC.                                  */
45 /******************************************************************/
GetDomDoc(PGLOBAL g,char * nsl,char * nsdf,char * enc,PFBLOCK fp)46 PXDOC GetDomDoc(PGLOBAL g, char *nsl, char *nsdf,
47                                       char *enc, PFBLOCK fp)
48   {
49   return (PXDOC) new(g) DOMDOC(nsl, nsdf, enc, fp);
50   }  // end of GetDomDoc
51 
52 /***********************************************************************/
53 /*  Close a loaded DOM XML file.                                       */
54 /***********************************************************************/
CloseXMLFile(PGLOBAL g,PFBLOCK fp,bool all)55 void CloseXMLFile(PGLOBAL g, PFBLOCK fp, bool all)
56   {
57   PXBLOCK xp = (PXBLOCK)fp;
58 
59   if (xp && xp->Count > 1 && !all) {
60     xp->Count--;
61   } else if (xp && xp->Count > 0) {
62     try {
63       if (xp->Docp)
64         xp->Docp->Release();
65 
66     } catch(_com_error e)  {
67 			char *p = _com_util::ConvertBSTRToString(e.Description());
68       sprintf(g->Message, "%s %s", MSG(COM_ERROR), p);
69 			delete[] p;
70     } catch(...) {}
71 
72     CoUninitialize();
73     xp->Count = 0;
74   }  // endif
75 
76   } // end of CloseXMLFile
77 
78 /* ------------------------ class DOMDOC ------------------------ */
79 
80 /******************************************************************/
81 /*  DOMDOC constructor.                                           */
82 /******************************************************************/
DOMDOC(char * nsl,char * nsdf,char * enc,PFBLOCK fp)83 DOMDOC::DOMDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp)
84       : XMLDOCUMENT(nsl, nsdf, enc)
85   {
86   assert (!fp || fp->Type == TYPE_FB_XML);
87   Docp = (fp) ? ((PXBLOCK)fp)->Docp : (MSXML2::IXMLDOMDocumentPtr)NULL;
88   Nlist = NULL;
89   Hr = 0;
90   } // end of DOMDOC constructor
91 
92 /******************************************************************/
93 /*  Initialize XML parser and check library compatibility.        */
94 /******************************************************************/
Initialize(PGLOBAL g,PCSZ entry,bool zipped)95 bool DOMDOC::Initialize(PGLOBAL g, PCSZ entry, bool zipped)
96 {
97 	if (zipped && InitZip(g, entry))
98 		return true;
99 
100 	if (TestHr(g, CoInitialize(NULL)))
101     return true;
102 
103   if (TestHr(g, Docp.CreateInstance("msxml2.domdocument")))
104     return true;
105 
106   return MakeNSlist(g);
107 } // end of Initialize
108 
109 /******************************************************************/
110 /* Parse the XML file and construct node tree in memory.          */
111 /******************************************************************/
ParseFile(PGLOBAL g,char * fn)112 bool DOMDOC::ParseFile(PGLOBAL g, char *fn)
113 {
114 	bool b;
115 
116   Docp->async = false;
117 
118 	if (zip) {
119 		// Parse an in memory document
120 		char *xdoc = GetMemDoc(g, fn);
121 
122 		// This is not equivalent to load for UTF8 characters
123 		// It is why get node content is not the same
124   	b = (xdoc) ? (bool)Docp->loadXML((_bstr_t)xdoc) : false;
125 	} else
126 		// Load the document
127 		b = (bool)Docp->load((_bstr_t)fn);
128 
129 	if (!b)
130     return true;
131 
132   return false;
133 } // end of ParseFile
134 
135 /******************************************************************/
136 /* Create or reuse an Xblock for this document.                   */
137 /******************************************************************/
LinkXblock(PGLOBAL g,MODE m,int rc,char * fn)138 PFBLOCK DOMDOC::LinkXblock(PGLOBAL g, MODE m, int rc, char *fn)
139   {
140   PDBUSER dup = (PDBUSER)g->Activityp->Aptr;
141   PXBLOCK xp = (PXBLOCK)PlugSubAlloc(g, NULL, sizeof(XBLOCK));
142 
143   memset(xp, 0, sizeof(XBLOCK));
144   xp->Next = (PXBLOCK)dup->Openlist;
145   dup->Openlist = (PFBLOCK)xp;
146   xp->Type = TYPE_FB_XML;
147   xp->Fname = (LPCSTR)PlugSubAlloc(g, NULL, strlen(fn) + 1);
148   strcpy((char*)xp->Fname, fn);
149   xp->Count = 1;
150   xp->Length = (m == MODE_READ) ? 1 : 0;
151   xp->Docp = Docp;
152   xp->Retcode = rc;
153 
154   // Return xp as a fp
155   return (PFBLOCK)xp;
156   } // end of LinkXblock
157 
158 /******************************************************************/
159 /* Create the XML node.                                           */
160 /******************************************************************/
NewDoc(PGLOBAL g,PCSZ ver)161 bool DOMDOC::NewDoc(PGLOBAL g, PCSZ ver)
162   {
163   char buf[64];
164   MSXML2::IXMLDOMProcessingInstructionPtr pip;
165 
166   sprintf(buf, "version=\"%s\" encoding=\"%s\"", ver, Encoding);
167   pip = Docp->createProcessingInstruction("xml", buf);
168   return(TestHr(g, Docp->appendChild(pip)));
169   } // end of NewDoc
170 
171 /******************************************************************/
172 /* Add a comment to the document node.                            */
173 /******************************************************************/
AddComment(PGLOBAL g,char * com)174 void DOMDOC::AddComment(PGLOBAL g, char *com)
175   {
176   TestHr(g, Docp->appendChild(Docp->createComment(com)));
177   } // end of AddComment
178 
179 /******************************************************************/
180 /* Return the node class of the root of the document.             */
181 /******************************************************************/
GetRoot(PGLOBAL g)182 PXNODE DOMDOC::GetRoot(PGLOBAL g)
183   {
184   MSXML2::IXMLDOMElementPtr root = Docp->documentElement;
185 
186   if (root == NULL)
187     return NULL;
188 
189   return new(g) DOMNODE(this, root);
190   } // end of GetRoot
191 
192 /******************************************************************/
193 /* Create a new root element and return its class node.           */
194 /******************************************************************/
NewRoot(PGLOBAL g,char * name)195 PXNODE DOMDOC::NewRoot(PGLOBAL g, char *name)
196   {
197   MSXML2::IXMLDOMElementPtr ep = Docp->createElement(name);
198 
199   if (ep == NULL || TestHr(g, Docp->appendChild(ep)))
200     return NULL;
201 
202   return new(g) DOMNODE(this, ep);
203   } // end of NewRoot
204 
205 /******************************************************************/
206 /* Return a void DOMNODE node class.                              */
207 /******************************************************************/
NewPnode(PGLOBAL g,char * name)208 PXNODE DOMDOC::NewPnode(PGLOBAL g, char *name)
209   {
210   MSXML2::IXMLDOMElementPtr root = NULL;
211 
212   if (name)
213     if ((root = Docp->createElement(name)) == NULL)
214       return NULL;
215 
216   return new(g) DOMNODE(this, root);
217   } // end of NewPnode
218 
219 /******************************************************************/
220 /* Return a void DOMATTR node class.                              */
221 /******************************************************************/
NewPattr(PGLOBAL g)222 PXATTR DOMDOC::NewPattr(PGLOBAL g)
223   {
224   return new(g) DOMATTR(this, NULL);
225   } // end of NewPattr
226 
227 /******************************************************************/
228 /* Return a void DOMATTR node class.                              */
229 /******************************************************************/
NewPlist(PGLOBAL g)230 PXLIST DOMDOC::NewPlist(PGLOBAL g)
231   {
232   return new(g) DOMNODELIST(this, NULL);
233   } // end of NewPlist
234 
235 /******************************************************************/
236 /* Dump the node tree to a new XML file.                          */
237 /******************************************************************/
DumpDoc(PGLOBAL g,char * ofn)238 int DOMDOC::DumpDoc(PGLOBAL g, char *ofn)
239   {
240   int rc = 0;
241 
242   try {
243     Docp->save(ofn);
244   } catch(_com_error e)  {
245     sprintf(g->Message, "%s: %s", MSG(COM_ERROR),
246             _com_util::ConvertBSTRToString(e.Description()));
247     rc = -1;
248   }  catch(...) {}
249 
250   return rc;
251   } // end of Dump
252 
253 /******************************************************************/
254 /* Free the document, cleanup the XML library, and                */
255 /* debug memory for regression tests.                             */
256 /******************************************************************/
CloseDoc(PGLOBAL g,PFBLOCK xp)257 void DOMDOC::CloseDoc(PGLOBAL g, PFBLOCK xp)
258   {
259   CloseXMLFile(g, xp, false);
260 	CloseZip();
261   } // end of Close
262 
263 /* ----------------------- class DOMNODE ------------------------ */
264 
265 /******************************************************************/
266 /*  DOMNODE constructor.                                          */
267 /******************************************************************/
DOMNODE(PXDOC dp,MSXML2::IXMLDOMNodePtr np)268 DOMNODE::DOMNODE(PXDOC dp, MSXML2::IXMLDOMNodePtr np) : XMLNODE(dp)
269   {
270   Docp = ((PDOMDOC)dp)->Docp;
271   Nodep = np;
272   Ws = NULL;
273   Len = 0;
274 	Zip = (bool)dp->zip;
275   } // end of DOMNODE constructor
276 
277 /******************************************************************/
278 /*  Return the node name.                                         */
279 /******************************************************************/
GetName(PGLOBAL g)280 char *DOMNODE::GetName(PGLOBAL g)
281   {
282   if (!WideCharToMultiByte(CP_ACP, 0, Nodep->nodeName, -1,
283                            Name, sizeof(Name), NULL, NULL)) {
284     strcpy(g->Message, MSG(NAME_CONV_ERR));
285     return NULL;
286     } // endif
287 
288   return Name;
289   }  // end of GetName
290 
291 /******************************************************************/
292 /*  Return the node class of next sibling of the node.            */
293 /******************************************************************/
GetNext(PGLOBAL g)294 PXNODE DOMNODE::GetNext(PGLOBAL g)
295   {
296   if (Nodep->nextSibling == NULL)
297     Next = NULL;
298   else // if (!Next)
299     Next = new(g) DOMNODE(Doc, Nodep->nextSibling);
300 
301   return Next;
302   } // end of GetNext
303 
304 /******************************************************************/
305 /*  Return the node class of first children of the node.          */
306 /******************************************************************/
GetChild(PGLOBAL g)307 PXNODE DOMNODE::GetChild(PGLOBAL g)
308   {
309   if (Nodep->firstChild == NULL)
310     Children = NULL;
311   else // if (!Children)
312     Children = new(g) DOMNODE(Doc, Nodep->firstChild);
313 
314   return Children;
315   } // end of GetChild
316 
317 /******************************************************************/
318 /*  Return the content of a node and subnodes.                    */
319 /******************************************************************/
GetContent(PGLOBAL g,char * buf,int len)320 RCODE DOMNODE::GetContent(PGLOBAL g, char *buf, int len)
321   {
322   RCODE rc = RC_OK;
323 
324   // Nodep can be null for a missing HTML table column
325   if (Nodep) {
326 		if (Zip) {
327 			strcpy(buf, Nodep->text);
328 		} else if (!WideCharToMultiByte(CP_UTF8, 0, Nodep->text, -1,
329                              buf, len, NULL, NULL)) {
330       DWORD lsr = GetLastError();
331 
332       switch (lsr) {
333         case 0:
334         case ERROR_INSUFFICIENT_BUFFER:      // 122L
335           sprintf(g->Message, "Truncated %s content", GetName(g));
336           rc = RC_INFO;
337           break;
338         case ERROR_NO_UNICODE_TRANSLATION:   // 1113L
339           sprintf(g->Message, "Invalid character(s) in %s content",
340                               GetName(g));
341           rc = RC_INFO;
342           break;
343         default:
344           sprintf(g->Message, "System error getting %s content",
345                               GetName(g));
346           rc = RC_FX;
347           break;
348         } // endswitch
349 
350       } // endif
351 
352   } else
353     *buf = '\0';
354 
355   return rc;
356   } // end of GetContent
357 
358 /******************************************************************/
359 /*  Set the text content of an attribute.                         */
360 /******************************************************************/
SetContent(PGLOBAL g,char * txtp,int len)361 bool DOMNODE::SetContent(PGLOBAL g, char *txtp, int len)
362   {
363   bool rc;
364   BSTR val;
365 
366   if (len > Len || !Ws) {
367     Ws = (WCHAR*)PlugSubAlloc(g, NULL, (len + 1) * 2);
368     Len = len;
369     } // endif len
370 
371   if (!MultiByteToWideChar(CP_UTF8, 0, txtp, strlen(txtp) + 1,
372                                        Ws, Len + 1)) {
373     sprintf(g->Message, MSG(WS_CONV_ERR), txtp);
374     return true;
375     } // endif
376 
377   val = SysAllocString(Ws);
378   rc = TestHr(g, Nodep->put_text(val));
379   SysFreeString(val);
380   return rc;
381   } // end of SetContent
382 
383 /******************************************************************/
384 /*  Return a clone of this node.                                  */
385 /******************************************************************/
Clone(PGLOBAL g,PXNODE np)386 PXNODE DOMNODE::Clone(PGLOBAL g, PXNODE np)
387   {
388   if (np) {
389     ((PDOMNODE)np)->Nodep = Nodep;
390     return np;
391   } else
392     return new(g) DOMNODE(Doc, Nodep);
393 
394   } // end of Clone
395 
396 /******************************************************************/
397 /*  Return the list of all or matching children that are elements.*/
398 /******************************************************************/
GetChildElements(PGLOBAL g,char * xp,PXLIST lp)399 PXLIST DOMNODE::GetChildElements(PGLOBAL g, char *xp, PXLIST lp)
400   {
401   MSXML2::IXMLDOMNodeListPtr dnlp;
402 
403   if (xp) {
404     if (Nodep->nodeType == MSXML2::NODE_ELEMENT) {
405       MSXML2::IXMLDOMElementPtr ep = Nodep;
406       dnlp = ep->getElementsByTagName(xp);
407     } else
408       return NULL;
409 
410   } else
411     dnlp = Nodep->childNodes;
412 
413   if (lp) {
414     ((PDOMLIST)lp)->Listp = dnlp;
415     return lp;
416   } else
417     return new(g) DOMNODELIST(Doc, dnlp);
418 
419   } // end of GetChildElements
420 
421 /******************************************************************/
422 /*  Return the list of nodes verifying the passed Xapth.          */
423 /******************************************************************/
SelectNodes(PGLOBAL g,char * xp,PXLIST lp)424 PXLIST DOMNODE::SelectNodes(PGLOBAL g, char *xp, PXLIST lp)
425   {
426   MSXML2::IXMLDOMNodeListPtr dnlp = Nodep->selectNodes(xp);
427 
428   if (lp) {
429     ((PDOMLIST)lp)->Listp = dnlp;
430     return lp;
431   } else
432     return new(g) DOMNODELIST(Doc, dnlp);
433 
434   } // end of SelectNodes
435 
436 /******************************************************************/
437 /*  Return the first node verifying the passed Xapth.             */
438 /******************************************************************/
SelectSingleNode(PGLOBAL g,char * xp,PXNODE np)439 PXNODE DOMNODE::SelectSingleNode(PGLOBAL g, char *xp, PXNODE np)
440   {
441   try {
442     MSXML2::IXMLDOMNodePtr dnp = Nodep->selectSingleNode(xp);
443 
444     if (dnp) {
445       if (np) {
446         ((PDOMNODE)np)->Nodep = dnp;
447         return np;
448       } else
449         return new(g) DOMNODE(Doc, dnp);
450 
451       } // endif dnp
452 
453   } catch(_com_error e) {
454     sprintf(g->Message, "%s: %s", MSG(COM_ERROR),
455             _com_util::ConvertBSTRToString(e.Description()));
456   } catch(...) {}
457 
458   return NULL;
459   } // end of SelectSingleNode
460 
461 /******************************************************************/
462 /*  Return the node attribute with the specified name.            */
463 /******************************************************************/
GetAttribute(PGLOBAL g,char * name,PXATTR ap)464 PXATTR DOMNODE::GetAttribute(PGLOBAL g, char *name, PXATTR ap)
465   {
466   MSXML2::IXMLDOMElementPtr ep;
467   MSXML2::IXMLDOMNamedNodeMapPtr nmp;
468   MSXML2::IXMLDOMAttributePtr atp;
469 
470   if (name) {
471     ep = Nodep;
472     atp = ep->getAttributeNode(name);
473     nmp = NULL;
474   } else {
475     nmp = Nodep->Getattributes();
476     atp = nmp->Getitem(0);
477   } // endif name
478 
479   if (atp) {
480     if (ap) {
481       ((PDOMATTR)ap)->Atrp = atp;
482       ((PDOMATTR)ap)->Nmp = nmp;
483       ((PDOMATTR)ap)->K = 0;
484       return ap;
485     } else
486       return new(g) DOMATTR(Doc, atp, nmp);
487 
488   } else
489     return NULL;
490 
491   } // end of GetAttribute
492 
493 /******************************************************************/
494 /*  Add a new element child node to this node and return it.      */
495 /******************************************************************/
AddChildNode(PGLOBAL g,PCSZ name,PXNODE np)496 PXNODE DOMNODE::AddChildNode(PGLOBAL g, PCSZ name, PXNODE np)
497   {
498   const char *p, *pn;
499 //  char *p, *pn, *epf, *pf = NULL;
500   MSXML2::IXMLDOMNodePtr ep;
501 //  _bstr_t   uri((wchar_t*)NULL);
502 
503 #if 0
504   // Is a prefix specified ?
505   if ((p = strchr(name, ':'))) {
506     pf = BufAlloc(g, name, p - name);
507 
508     // Is it the pseudo default prefix
509     if (Doc->DefNs && !strcmp(pf, Doc->DefNs)) {
510       name = p + 1;              // Suppress it from name
511       pf = NULL;                // No real prefix
512       } // endif DefNs
513 
514     } // endif p
515 
516   // Look for matching namespace URI in context
517   for (ep = Nodep; ep; ep = ep->parentNode) {
518     epf = (_bstr_t)ep->prefix;
519 
520     if ((!pf && !epf) || (pf && epf && !strcmp(pf, epf))) {
521       uri = Nodep->namespaceURI;
522       break;
523       } // endif
524 
525     } // endfor ep
526 
527   if ((wchar_t*)uri == NULL) {
528     if (!pf)
529       pf = Doc->DefNs;
530 
531     // Look for the namespace URI corresponding to this node
532     if (pf)
533       for (PNS nsp = Doc->Namespaces; nsp; nsp = nsp->Next)
534         if (!strcmp(pf, nsp->Prefix)) {
535           uri = nsp->Uri;
536           break;
537           } // endfor nsp
538 
539     } // endif pns
540 #endif // 0
541 
542   // If name has the format m[n] only m is taken as node name
543   if ((p = strchr(name, '[')))
544     pn = BufAlloc(g, name, (int)(p - name));
545   else
546     pn = name;
547 
548   // Construct the element node with eventual namespace
549 //  ep = Docp->createNode(_variant_t("Element"), pn, uri);
550   ep = Docp->createElement(pn);
551 
552   _bstr_t pfx = ep->prefix;
553   _bstr_t uri = ep->namespaceURI;
554 
555   if (ep == NULL || TestHr(g, Nodep->appendChild(ep)))
556     return NULL;
557 
558   if (np)
559     ((PDOMNODE)np)->Nodep = ep;
560   else
561     np = new(g) DOMNODE(Doc, ep);
562 
563   return NewChild(np);
564   } // end of AddChildNode
565 
566 /******************************************************************/
567 /*  Add a new property to this node and return it.                */
568 /******************************************************************/
AddProperty(PGLOBAL g,char * name,PXATTR ap)569 PXATTR DOMNODE::AddProperty(PGLOBAL g, char *name, PXATTR ap)
570   {
571   MSXML2::IXMLDOMAttributePtr atp = Docp->createAttribute(name);
572 
573   if (atp) {
574     MSXML2::IXMLDOMElementPtr ep = Nodep;
575     ep->setAttributeNode(atp);
576 
577     if (ap) {
578       ((PDOMATTR)ap)->Atrp = atp;
579       return ap;
580     } else
581       return new(g) DOMATTR(Doc, atp);
582 
583   } else
584     return NULL;
585 
586   } // end of AddProperty
587 
588 /******************************************************************/
589 /*  Add a new text node to this node.                             */
590 /******************************************************************/
AddText(PGLOBAL g,PCSZ txtp)591 void DOMNODE::AddText(PGLOBAL g, PCSZ txtp)
592   {
593   MSXML2::IXMLDOMTextPtr tp= Docp->createTextNode((_bstr_t)txtp);
594 
595   if (tp != NULL)
596     TestHr(g, Nodep->appendChild(tp));
597 
598   } // end of AddText
599 
600 /******************************************************************/
601 /*  Remove a child node from this node.                           */
602 /******************************************************************/
DeleteChild(PGLOBAL g,PXNODE dnp)603 void DOMNODE::DeleteChild(PGLOBAL g, PXNODE dnp)
604   {
605   TestHr(g, Nodep->removeChild(((PDOMNODE)dnp)->Nodep));
606 //  ((PDOMNODE)dnp)->Nodep->Release();  bad idea, causes a crash
607   Delete(dnp);
608   } // end of DeleteChild
609 
610 /* --------------------- class DOMNODELIST ---------------------- */
611 
612 /******************************************************************/
613 /*  DOMNODELIST constructor.                                      */
614 /******************************************************************/
DOMNODELIST(PXDOC dp,MSXML2::IXMLDOMNodeListPtr lp)615 DOMNODELIST::DOMNODELIST(PXDOC dp, MSXML2::IXMLDOMNodeListPtr lp)
616            : XMLNODELIST(dp)
617   {
618   Listp = lp;
619   } // end of DOMNODELIST constructor
620 
621 /******************************************************************/
622 /*  Return the nth element of the list.                           */
623 /******************************************************************/
GetItem(PGLOBAL g,int n,PXNODE np)624 PXNODE DOMNODELIST::GetItem(PGLOBAL g, int n, PXNODE np)
625   {
626   if (Listp == NULL || Listp->length <= n)
627     return NULL;
628 
629   if (np) {
630     ((PDOMNODE)np)->Nodep = Listp->item[n];
631     return np;
632   } else
633     return new(g) DOMNODE(Doc, Listp->item[n]);
634 
635   }  // end of GetItem
636 
637 /******************************************************************/
638 /*  Reset the pointer on the deleted item.                        */
639 /******************************************************************/
DropItem(PGLOBAL g,int n)640 bool DOMNODELIST::DropItem(PGLOBAL g, int n)
641 {
642 	if (Listp == NULL || Listp->length < n)
643 		return true;
644 
645 //Listp->item[n] = NULL;  La propri�t� n'a pas de m�thode 'set'
646   return false;
647 }  // end of DeleteItem
648 
649 /* ----------------------- class DOMATTR ------------------------ */
650 
651 /******************************************************************/
652 /*  DOMATTR constructor.                                          */
653 /******************************************************************/
DOMATTR(PXDOC dp,MSXML2::IXMLDOMAttributePtr ap,MSXML2::IXMLDOMNamedNodeMapPtr nmp)654 DOMATTR::DOMATTR(PXDOC dp, MSXML2::IXMLDOMAttributePtr ap,
655                            MSXML2::IXMLDOMNamedNodeMapPtr nmp)
656         : XMLATTRIBUTE(dp)
657   {
658   Atrp = ap;
659   Nmp = nmp;
660   Ws = NULL;
661   Len = 0;
662   K = 0;
663   }  // end of DOMATTR constructor
664 
665 /******************************************************************/
666 /*  Return the attribute name.                                    */
667 /******************************************************************/
GetName(PGLOBAL g)668 char *DOMATTR::GetName(PGLOBAL g)
669   {
670   if (!WideCharToMultiByte(CP_ACP, 0, Atrp->nodeName, -1,
671                            Name, sizeof(Name), NULL, NULL)) {
672     strcpy(g->Message, MSG(NAME_CONV_ERR));
673     return NULL;
674     } // endif
675 
676   return Name;
677   }  // end of GetName
678 
679 /******************************************************************/
680 /*  Return the next attribute node.                               */
681 /*  This funtion is implemented as needed by XMLColumns.          */
682 /******************************************************************/
GetNext(PGLOBAL g)683 PXATTR DOMATTR::GetNext(PGLOBAL g)
684   {
685   if (!Nmp)
686     return NULL;
687 
688   if (++K >= Nmp->Getlength()) {
689     Nmp->reset();
690     Nmp = NULL;
691     K = 0;
692     return NULL;
693     } // endif K
694 
695   Atrp = Nmp->Getitem(K);
696   return this;
697   } // end of GetNext
698 
699 /******************************************************************/
700 /*  Return the content of a node and subnodes.                    */
701 /******************************************************************/
GetText(PGLOBAL g,char * buf,int len)702 RCODE DOMATTR::GetText(PGLOBAL g, char *buf, int len)
703   {
704   RCODE rc = RC_OK;
705 
706   if (!WideCharToMultiByte(CP_UTF8, 0, Atrp->text, -1,
707                            buf, len, NULL, NULL)) {
708     DWORD lsr = GetLastError();
709 
710     switch (lsr) {
711       case 0:
712       case ERROR_INSUFFICIENT_BUFFER:      // 122L
713         sprintf(g->Message, "Truncated %s content", GetName(g));
714         rc = RC_INFO;
715         break;
716       case ERROR_NO_UNICODE_TRANSLATION:   // 1113L
717         sprintf(g->Message, "Invalid character(s) in %s content",
718                             GetName(g));
719         rc = RC_INFO;
720         break;
721       default:
722         sprintf(g->Message, "System error getting %s content",
723                             GetName(g));
724         rc = RC_FX;
725         break;
726       } // endswitch
727 
728     } // endif
729 
730   return rc;
731   } // end of GetText
732 
733 /******************************************************************/
734 /*  Set the text content of an attribute.                         */
735 /******************************************************************/
SetText(PGLOBAL g,char * txtp,int len)736 bool DOMATTR::SetText(PGLOBAL g, char *txtp, int len)
737   {
738   bool rc;
739   BSTR val;
740 
741   if (len > Len || !Ws) {
742     Ws = (WCHAR*)PlugSubAlloc(g, NULL, (len + 1) * 2);
743     Len = len;
744     } // endif len
745 
746   if (!MultiByteToWideChar(CP_UTF8, 0, txtp, strlen(txtp) + 1,
747                                        Ws, Len + 1)) {
748     sprintf(g->Message, MSG(WS_CONV_ERR), txtp);
749     return true;
750     } // endif
751 
752   val = SysAllocString(Ws);
753   rc = TestHr(g, Atrp->put_text(val));
754   SysFreeString(val);
755   return rc;
756   } // end of SetText
757