1 | /******************************************************************/ |
2 | /* Implementation of XML document processing using libxml2 */ |
3 | /* Author: Olivier Bertrand 2007-2016 */ |
4 | /******************************************************************/ |
5 | #include "my_global.h" |
6 | #include <string.h> |
7 | #include <stdio.h> |
8 | #include <libxml/parser.h> |
9 | #include <libxml/tree.h> |
10 | #include <libxml/xpath.h> |
11 | #include <libxml/xpathInternals.h> |
12 | #include <libxml/catalog.h> |
13 | #include <libxml/xmlschemastypes.h> |
14 | #include <libxml/relaxng.h> |
15 | |
16 | #if !defined(LIBXML_TREE_ENABLED) || !defined(LIBXML_OUTPUT_ENABLED) |
17 | #error "tree support not compiled in" |
18 | #endif |
19 | |
20 | #if !defined(LIBXML_XPATH_ENABLED) || !defined(LIBXML_SAX1_ENABLED) |
21 | #error "XPath not supported" |
22 | #endif |
23 | |
24 | #include "global.h" |
25 | #include "plgdbsem.h" |
26 | #include "xobject.h" |
27 | #include "libdoc.h" |
28 | |
29 | #include "sql_string.h" |
30 | |
31 | /******************************************************************/ |
32 | /* Declaration of XML document processing using libxml2 */ |
33 | /* Author: Olivier Bertrand 2007-2012 */ |
34 | /******************************************************************/ |
35 | #include "plgxml.h" |
36 | |
37 | typedef class LIBXMLDOC *PXDOC2; |
38 | typedef class XML2NODE *PNODE2; |
39 | typedef class XML2ATTR *PATTR2; |
40 | typedef class XML2NODELIST *PLIST2; |
41 | |
42 | /******************************************************************/ |
43 | /* XML2 block. Must have the same layout than FBLOCK up to Type. */ |
44 | /******************************************************************/ |
45 | typedef struct _x2block { /* Loaded XML file block */ |
46 | struct _x2block *Next; |
47 | LPCSTR Fname; /* Point on file name */ |
48 | size_t Length; /* Used to tell if read mode */ |
49 | short Count; /* Nb of times file is used */ |
50 | short Type; /* TYPE_FB_XML */ |
51 | int Retcode; /* Return code from Load */ |
52 | xmlDocPtr Docp; /* Document interface pointer */ |
53 | } X2BLOCK, *PX2BLOCK; |
54 | |
55 | /******************************************************************/ |
56 | /* Declaration of libxml2 document. */ |
57 | /******************************************************************/ |
58 | class LIBXMLDOC : public XMLDOCUMENT { |
59 | friend class XML2NODE; |
60 | friend class XML2ATTR; |
61 | public: |
62 | // Constructor |
63 | LIBXMLDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp); |
64 | |
65 | // Properties |
66 | virtual short GetDocType(void) {return TYPE_FB_XML2;} |
67 | virtual void *GetDocPtr(void) {return Docp;} |
68 | virtual void SetNofree(bool b) {Nofreelist = b;} |
69 | |
70 | // Methods |
71 | virtual bool Initialize(PGLOBAL g, PCSZ entry, bool zipped); |
72 | virtual bool ParseFile(PGLOBAL g, char *fn); |
73 | virtual bool NewDoc(PGLOBAL g, PCSZ ver); |
74 | virtual void AddComment(PGLOBAL g, char *com); |
75 | virtual PXNODE GetRoot(PGLOBAL g); |
76 | virtual PXNODE NewRoot(PGLOBAL g, char *name); |
77 | virtual PXNODE NewPnode(PGLOBAL g, char *name); |
78 | virtual PXATTR NewPattr(PGLOBAL g); |
79 | virtual PXLIST NewPlist(PGLOBAL g); |
80 | virtual int DumpDoc(PGLOBAL g, char *ofn); |
81 | virtual void CloseDoc(PGLOBAL g, PFBLOCK xp); |
82 | virtual PFBLOCK LinkXblock(PGLOBAL g, MODE m, int rc, char *fn); |
83 | |
84 | protected: |
85 | // bool CheckDocument(FILE *of, xmlNodePtr np); |
86 | xmlNodeSetPtr GetNodeList(PGLOBAL g, xmlNodePtr np, char *xp); |
87 | int Decode(xmlChar *cnt, char *buf, int n); |
88 | xmlChar *Encode(PGLOBAL g, char *txt); |
89 | |
90 | // Members |
91 | xmlDocPtr Docp; |
92 | xmlNodeSetPtr Nlist; |
93 | xmlXPathContextPtr Ctxp; |
94 | xmlXPathObjectPtr Xop; |
95 | xmlXPathObjectPtr NlXop; |
96 | xmlErrorPtr Xerr; |
97 | char *Buf; // Temporary |
98 | bool Nofreelist; |
99 | }; // end of class LIBXMLDOC |
100 | |
101 | /******************************************************************/ |
102 | /* Declaration of libxml2 node. */ |
103 | /******************************************************************/ |
104 | class XML2NODE : public XMLNODE { |
105 | friend class LIBXMLDOC; |
106 | friend class XML2NODELIST; |
107 | public: |
108 | // Properties |
109 | virtual char *GetName(PGLOBAL g) {return (char*)Nodep->name;} |
110 | virtual int GetType(void); |
111 | virtual PXNODE GetNext(PGLOBAL g); |
112 | virtual PXNODE GetChild(PGLOBAL g); |
113 | |
114 | // Methods |
115 | virtual RCODE GetContent(PGLOBAL g, char *buf, int len); |
116 | virtual bool SetContent(PGLOBAL g, char *txtp, int len); |
117 | virtual PXNODE Clone(PGLOBAL g, PXNODE np); |
118 | virtual PXLIST GetChildElements(PGLOBAL g, char *xp, PXLIST lp); |
119 | virtual PXLIST SelectNodes(PGLOBAL g, char *xp, PXLIST lp); |
120 | virtual PXNODE SelectSingleNode(PGLOBAL g, char *xp, PXNODE np); |
121 | virtual PXATTR GetAttribute(PGLOBAL g, char *name, PXATTR ap); |
122 | virtual PXNODE AddChildNode(PGLOBAL g, PCSZ name, PXNODE np); |
123 | virtual PXATTR AddProperty(PGLOBAL g, char *name, PXATTR ap); |
124 | virtual void AddText(PGLOBAL g, PCSZ txtp); |
125 | virtual void DeleteChild(PGLOBAL g, PXNODE dnp); |
126 | |
127 | protected: |
128 | // Constructor |
129 | XML2NODE(PXDOC dp, xmlNodePtr np); |
130 | |
131 | // Members |
132 | xmlDocPtr Docp; |
133 | xmlChar *Content; |
134 | xmlNodePtr Nodep; |
135 | }; // end of class XML2NODE |
136 | |
137 | /******************************************************************/ |
138 | /* Declaration of libxml2 node list. */ |
139 | /******************************************************************/ |
140 | class XML2NODELIST : public XMLNODELIST { |
141 | friend class LIBXMLDOC; |
142 | friend class XML2NODE; |
143 | public: |
144 | // Methods |
145 | virtual int GetLength(void); |
146 | virtual PXNODE GetItem(PGLOBAL g, int n, PXNODE np); |
147 | virtual bool DropItem(PGLOBAL g, int n); |
148 | |
149 | protected: |
150 | // Constructor |
151 | XML2NODELIST(PXDOC dp, xmlNodeSetPtr lp); |
152 | |
153 | // Members |
154 | xmlNodeSetPtr Listp; |
155 | }; // end of class XML2NODELIST |
156 | |
157 | /******************************************************************/ |
158 | /* Declaration of libxml2 attribute. */ |
159 | /******************************************************************/ |
160 | class XML2ATTR : public XMLATTRIBUTE { |
161 | friend class LIBXMLDOC; |
162 | friend class XML2NODE; |
163 | public: |
164 | // Properties |
165 | virtual char *GetName(PGLOBAL g) {return (char*)Atrp->name;} |
166 | virtual PXATTR GetNext(PGLOBAL g); |
167 | |
168 | // Methods |
169 | virtual RCODE GetText(PGLOBAL g, char *bufp, int len); |
170 | virtual bool SetText(PGLOBAL g, char *txtp, int len); |
171 | |
172 | protected: |
173 | // Constructor |
174 | XML2ATTR(PXDOC dp, xmlAttrPtr ap, xmlNodePtr np); |
175 | |
176 | // Members |
177 | xmlAttrPtr Atrp; |
178 | xmlNodePtr Parent; |
179 | }; // end of class XML2ATTR |
180 | |
181 | |
182 | |
183 | extern "C" { |
184 | extern char version[]; |
185 | } // "C" |
186 | |
187 | #if defined(MEMORY_TRACE) |
188 | static int m = 0; |
189 | static char s[500]; |
190 | /**************************************************************************/ |
191 | /* Tracing output function. */ |
192 | /**************************************************************************/ |
193 | void xtrc(char const *fmt, ...) |
194 | { |
195 | va_list ap; |
196 | va_start (ap, fmt); |
197 | ; |
198 | //vfprintf(stderr, fmt, ap); |
199 | vsprintf(s, fmt, ap); |
200 | if (s[strlen(s)-1] == '\n') |
201 | s[strlen(s)-1] = 0; |
202 | va_end (ap); |
203 | } // end of htrc |
204 | |
205 | static xmlFreeFunc Free; |
206 | static xmlMallocFunc Malloc; |
207 | static xmlMallocFunc MallocA; |
208 | static xmlReallocFunc Realloc; |
209 | static xmlStrdupFunc Strdup; |
210 | |
211 | void xmlMyFree(void *mem) |
212 | { |
213 | if (trace(1)) { |
214 | htrc("%.4d Freeing at %p %s\n" , ++m, mem, s); |
215 | *s = 0; |
216 | } // endif trace |
217 | Free(mem); |
218 | } // end of xmlMyFree |
219 | |
220 | void *xmlMyMalloc(size_t size) |
221 | { |
222 | void *p = Malloc(size); |
223 | if (trace(1)) { |
224 | htrc("%.4d Allocating %.5d at %p %s\n" , ++m, size, p, s); |
225 | *s = 0; |
226 | } // endif trace |
227 | return p; |
228 | } // end of xmlMyMalloc |
229 | |
230 | void *xmlMyMallocAtomic(size_t size) |
231 | { |
232 | void *p = MallocA(size); |
233 | if (trace(1)) { |
234 | htrc("%.4d Atom alloc %.5d at %p %s\n" , ++m, size, p, s); |
235 | *s = 0; |
236 | } // endif trace |
237 | return p; |
238 | } // end of xmlMyMallocAtomic |
239 | |
240 | void *xmlMyRealloc(void *mem, size_t size) |
241 | { |
242 | void *p = Realloc(mem, size); |
243 | if (trace(1)) { |
244 | htrc("%.4d ReAlloc %.5d to %p from %p %s\n" , ++m, size, p, mem, s); |
245 | *s = 0; |
246 | } // endif trace |
247 | return p; |
248 | } // end of xmlMyRealloc |
249 | |
250 | char *xmlMyStrdup(const char *str) |
251 | { |
252 | char *p = Strdup(str); |
253 | if (trace(1)) { |
254 | htrc("%.4d Duplicating to %p from %p %s %s\n" , ++m, p, str, str, s); |
255 | *s = 0; |
256 | } // endif trace |
257 | return p; |
258 | } // end of xmlMyStrdup |
259 | #define htrc xtrc |
260 | #endif // MEMORY_TRACE |
261 | |
262 | /******************************************************************/ |
263 | /* Return a LIBXMLDOC as a XMLDOC. */ |
264 | /******************************************************************/ |
265 | PXDOC GetLibxmlDoc(PGLOBAL g, char *nsl, char *nsdf, |
266 | char *enc, PFBLOCK fp) |
267 | { |
268 | return (PXDOC) new(g) LIBXMLDOC(nsl, nsdf, enc, fp); |
269 | } // end of GetLibxmlDoc |
270 | |
271 | /******************************************************************/ |
272 | /* XML library initialization function. */ |
273 | /******************************************************************/ |
274 | void XmlInitParserLib(void) |
275 | { |
276 | #if defined(MEMORY_TRACE) |
277 | int rc = xmlGcMemGet(&Free, &Malloc, &MallocA, &Realloc, &Strdup); |
278 | |
279 | if (!rc) |
280 | rc = xmlGcMemSetup(xmlMyFree, |
281 | xmlMyMalloc, |
282 | xmlMyMallocAtomic, |
283 | xmlMyRealloc, |
284 | xmlMyStrdup); |
285 | |
286 | #endif // MEMORY_TRACE |
287 | xmlInitParser(); |
288 | } // end of XmlInitParserLib |
289 | |
290 | /******************************************************************/ |
291 | /* XML library cleanup function. */ |
292 | /******************************************************************/ |
293 | /* |
294 | This is a copy of xmlCleanupParser() from the libxml2 sources |
295 | with xmlResetLastError() commented. |
296 | |
297 | xmlResetLastError() called from the original xmlCleanupParser() causes |
298 | valgrind to report memory leaks. This happens because |
299 | ha_initialize_handlerton() is called from the main thread in mysqld.cc, |
300 | while ha_finalize_handlerton() is called from a non-main thread. |
301 | libxml2 gets confused because of xmlInitParser() and xmlCleanupParser() |
302 | being called from the different threads. |
303 | |
304 | Perhaps the code in mysqld.cc should eventually be modified |
305 | to shutdown plugins from the main thread. |
306 | */ |
307 | static void |
308 | xmlCleanupParser_replacement(void) |
309 | { |
310 | xmlCleanupCharEncodingHandlers(); |
311 | #ifdef LIBXML_CATALOG_ENABLED |
312 | xmlCatalogCleanup(); |
313 | #endif |
314 | xmlDictCleanup(); |
315 | xmlCleanupInputCallbacks(); |
316 | #ifdef LIBXML_OUTPUT_ENABLED |
317 | xmlCleanupOutputCallbacks(); |
318 | #endif |
319 | #ifdef LIBXML_SCHEMAS_ENABLED |
320 | xmlSchemaCleanupTypes(); |
321 | xmlRelaxNGCleanupTypes(); |
322 | #endif |
323 | //xmlResetLastError(); |
324 | xmlCleanupGlobals(); |
325 | xmlCleanupThreads(); /* must be last if called not from the main thread */ |
326 | xmlCleanupMemory(); |
327 | } |
328 | |
329 | |
330 | void XmlCleanupParserLib(void) |
331 | { |
332 | xmlCleanupParser_replacement(); |
333 | } // end of XmlCleanupParserLib |
334 | |
335 | /******************************************************************/ |
336 | /* Close a loaded libxml2 XML file. */ |
337 | /******************************************************************/ |
338 | void CloseXML2File(PGLOBAL g, PFBLOCK fp, bool all) |
339 | { |
340 | PX2BLOCK xp = (PX2BLOCK)fp; |
341 | |
342 | if (trace(1)) |
343 | htrc("CloseXML2File: xp=%p count=%d\n" , xp, (xp) ? xp->Count : 0); |
344 | |
345 | if (xp && xp->Count > 1 && !all) { |
346 | xp->Count--; |
347 | } else if (xp && xp->Count > 0) { |
348 | xmlFreeDoc(xp->Docp); |
349 | xp->Count = 0; |
350 | } // endif |
351 | |
352 | } // end of CloseXML2File |
353 | |
354 | /* ---------------------- class LIBXMLDOC ----------------------- */ |
355 | |
356 | /******************************************************************/ |
357 | /* LIBXMLDOC constructor. */ |
358 | /******************************************************************/ |
359 | LIBXMLDOC::LIBXMLDOC(char *nsl, char *nsdf, char *enc, PFBLOCK fp) |
360 | : XMLDOCUMENT(nsl, nsdf, enc) |
361 | { |
362 | assert (!fp || fp->Type == TYPE_FB_XML2); |
363 | Docp = (fp) ? ((PX2BLOCK)fp)->Docp : NULL; |
364 | Nlist = NULL; |
365 | Ctxp = NULL; |
366 | Xop = NULL; |
367 | NlXop = NULL; |
368 | Xerr = NULL; |
369 | Buf = NULL; |
370 | Nofreelist = false; |
371 | } // end of LIBXMLDOC constructor |
372 | |
373 | /******************************************************************/ |
374 | /* Initialize XML parser and check library compatibility. */ |
375 | /******************************************************************/ |
376 | bool LIBXMLDOC::Initialize(PGLOBAL g, PCSZ entry, bool zipped) |
377 | { |
378 | if (zipped && InitZip(g, entry)) |
379 | return true; |
380 | |
381 | int n = xmlKeepBlanksDefault(1); |
382 | return MakeNSlist(g); |
383 | } // end of Initialize |
384 | |
385 | /******************************************************************/ |
386 | /* Parse the XML file and construct node tree in memory. */ |
387 | /******************************************************************/ |
388 | bool LIBXMLDOC::ParseFile(PGLOBAL g, char *fn) |
389 | { |
390 | if (trace(1)) |
391 | htrc("ParseFile\n" ); |
392 | |
393 | if (zip) { |
394 | // Parse an in memory document |
395 | char *xdoc = GetMemDoc(g, fn); |
396 | |
397 | Docp = (xdoc) ? xmlParseDoc((const xmlChar *)xdoc) : NULL; |
398 | } else |
399 | Docp = xmlParseFile(fn); |
400 | |
401 | if (Docp) { |
402 | if (Docp->encoding) |
403 | Encoding = (char*)Docp->encoding; |
404 | |
405 | return false; |
406 | } else if ((Xerr = xmlGetLastError())) |
407 | xmlResetError(Xerr); |
408 | |
409 | return true; |
410 | } // end of ParseFile |
411 | |
412 | /******************************************************************/ |
413 | /* Create or reuse an Xblock for this document. */ |
414 | /******************************************************************/ |
415 | PFBLOCK LIBXMLDOC::LinkXblock(PGLOBAL g, MODE m, int rc, char *fn) |
416 | { |
417 | PDBUSER dup = (PDBUSER)g->Activityp->Aptr; |
418 | PX2BLOCK xp = (PX2BLOCK)PlugSubAlloc(g, NULL, sizeof(X2BLOCK)); |
419 | |
420 | memset(xp, 0, sizeof(X2BLOCK)); |
421 | xp->Next = (PX2BLOCK)dup->Openlist; |
422 | dup->Openlist = (PFBLOCK)xp; |
423 | xp->Type = TYPE_FB_XML2; |
424 | xp->Fname = (LPCSTR)PlugDup(g, fn); |
425 | xp->Count = 1; |
426 | xp->Length = (m == MODE_READ) ? 1 : 0; |
427 | xp->Retcode = rc; |
428 | xp->Docp = Docp; |
429 | |
430 | // Return xp as a fp |
431 | return (PFBLOCK)xp; |
432 | } // end of LinkXblock |
433 | |
434 | /******************************************************************/ |
435 | /* Construct and add the XML processing instruction node. */ |
436 | /******************************************************************/ |
437 | bool LIBXMLDOC::NewDoc(PGLOBAL g, PCSZ ver) |
438 | { |
439 | if (trace(1)) |
440 | htrc("NewDoc\n" ); |
441 | |
442 | return ((Docp = xmlNewDoc(BAD_CAST ver)) == NULL); |
443 | } // end of NewDoc |
444 | |
445 | /******************************************************************/ |
446 | /* Add a new comment node to the document. */ |
447 | /******************************************************************/ |
448 | void LIBXMLDOC::(PGLOBAL g, char *txtp) |
449 | { |
450 | if (trace(1)) |
451 | htrc("AddComment: %s\n" , txtp); |
452 | |
453 | xmlNodePtr cp = xmlNewDocComment(Docp, BAD_CAST txtp); |
454 | xmlAddChild((xmlNodePtr)Docp, cp); |
455 | } // end of AddText |
456 | |
457 | /******************************************************************/ |
458 | /* Return the node class of the root of the document. */ |
459 | /******************************************************************/ |
460 | PXNODE LIBXMLDOC::GetRoot(PGLOBAL g) |
461 | { |
462 | if (trace(1)) |
463 | htrc("GetRoot\n" ); |
464 | |
465 | xmlNodePtr root = xmlDocGetRootElement(Docp); |
466 | |
467 | if (!root) |
468 | return NULL; |
469 | |
470 | return new(g) XML2NODE(this, root); |
471 | } // end of GetRoot |
472 | |
473 | /******************************************************************/ |
474 | /* Create a new root element and return its class node. */ |
475 | /******************************************************************/ |
476 | PXNODE LIBXMLDOC::NewRoot(PGLOBAL g, char *name) |
477 | { |
478 | if (trace(1)) |
479 | htrc("NewRoot: %s\n" , name); |
480 | |
481 | xmlNodePtr root = xmlNewDocNode(Docp, NULL, BAD_CAST name, NULL); |
482 | |
483 | if (root) { |
484 | xmlDocSetRootElement(Docp, root); |
485 | return new(g) XML2NODE(this, root); |
486 | } else |
487 | return NULL; |
488 | |
489 | } // end of NewRoot |
490 | |
491 | /******************************************************************/ |
492 | /* Return a void XML2NODE node class. */ |
493 | /******************************************************************/ |
494 | PXNODE LIBXMLDOC::NewPnode(PGLOBAL g, char *name) |
495 | { |
496 | if (trace(1)) |
497 | htrc("NewNode: %s\n" , name); |
498 | |
499 | xmlNodePtr nop; |
500 | |
501 | if (name) { |
502 | nop = xmlNewDocNode(Docp, NULL, BAD_CAST name, NULL); |
503 | |
504 | if (nop == NULL) |
505 | return NULL; |
506 | |
507 | } else |
508 | nop = NULL; |
509 | |
510 | return new(g) XML2NODE(this, nop); |
511 | } // end of NewPnode |
512 | |
513 | /******************************************************************/ |
514 | /* Return a void XML2ATTR node class. */ |
515 | /******************************************************************/ |
516 | PXATTR LIBXMLDOC::NewPattr(PGLOBAL g) |
517 | { |
518 | return new(g) XML2ATTR(this, NULL, NULL); |
519 | } // end of NewPattr |
520 | |
521 | /******************************************************************/ |
522 | /* Return a void XML2ATTR node class. */ |
523 | /******************************************************************/ |
524 | PXLIST LIBXMLDOC::NewPlist(PGLOBAL g) |
525 | { |
526 | return new(g) XML2NODELIST(this, NULL); |
527 | } // end of NewPlist |
528 | |
529 | /******************************************************************/ |
530 | /* Dump the node tree to a new XML file. */ |
531 | /******************************************************************/ |
532 | int LIBXMLDOC::DumpDoc(PGLOBAL g, char *ofn) |
533 | { |
534 | int rc = 0; |
535 | FILE *of; |
536 | |
537 | if (trace(1)) |
538 | htrc("DumpDoc: %s\n" , ofn); |
539 | |
540 | if (!(of= global_fopen(g, MSGID_CANNOT_OPEN, ofn, "w" ))) |
541 | return -1; |
542 | |
543 | #if 1 |
544 | // This function does not crash ( |
545 | if (xmlSaveFormatFileEnc((const char *)ofn, Docp, Encoding, 0) < 0) { |
546 | xmlErrorPtr err = xmlGetLastError(); |
547 | strcpy(g->Message, (err) ? err->message : "Error saving XML doc" ); |
548 | xmlResetError(Xerr); |
549 | rc = -1; |
550 | } // endif Save |
551 | // rc = xmlDocDump(of, Docp); |
552 | #else // 0 |
553 | // Until this function is fixed, do the job ourself |
554 | xmlNodePtr Rootp; |
555 | |
556 | // Save the modified document |
557 | fprintf(of, "<?xml version=\"1.0\" encoding=\"%s\"?>\n" , Encoding); |
558 | fprintf(of, "<!-- Created by CONNECT %s -->\n" , version); |
559 | |
560 | if (!(Rootp = xmlDocGetRootElement(Docp))) |
561 | return 1; |
562 | |
563 | Buf = (char*)PlugSubAlloc(g, NULL, 1024); |
564 | rc = iconv_close(Cd2); |
565 | Cd2 = iconv_open(Encoding, "UTF-8" ); |
566 | rc = CheckDocument(of, Rootp); |
567 | #endif // 0 |
568 | |
569 | fclose(of); |
570 | return rc; |
571 | } // end of Dump |
572 | |
573 | /******************************************************************/ |
574 | /* Free the document, cleanup the XML library, and */ |
575 | /* debug memory for regression tests. */ |
576 | /******************************************************************/ |
577 | void LIBXMLDOC::CloseDoc(PGLOBAL g, PFBLOCK xp) |
578 | { |
579 | if (trace(1)) |
580 | htrc("CloseDoc: xp=%p count=%d\n" , xp, (xp) ? xp->Count : 0); |
581 | |
582 | //if (xp && xp->Count == 1) { |
583 | if (xp) { |
584 | if (Nlist) { |
585 | xmlXPathFreeNodeSet(Nlist); |
586 | |
587 | if ((Xerr = xmlGetLastError())) |
588 | xmlResetError(Xerr); |
589 | |
590 | Nlist = NULL; |
591 | } // endif Nlist |
592 | |
593 | if (Xop) { |
594 | xmlXPathFreeObject(Xop); |
595 | |
596 | if ((Xerr = xmlGetLastError())) |
597 | xmlResetError(Xerr); |
598 | |
599 | Xop = NULL; |
600 | } // endif Xop |
601 | |
602 | if (NlXop) { |
603 | xmlXPathFreeObject(NlXop); |
604 | |
605 | if ((Xerr = xmlGetLastError())) |
606 | xmlResetError(Xerr); |
607 | |
608 | NlXop = NULL; |
609 | } // endif NlXop |
610 | |
611 | if (Ctxp) { |
612 | xmlXPathFreeContext(Ctxp); |
613 | |
614 | if ((Xerr = xmlGetLastError())) |
615 | xmlResetError(Xerr); |
616 | |
617 | Ctxp = NULL; |
618 | } // endif Ctxp |
619 | |
620 | } // endif xp |
621 | |
622 | CloseXML2File(g, xp, false); |
623 | CloseZip(); |
624 | } // end of Close |
625 | |
626 | /******************************************************************/ |
627 | /* Evaluate the passed Xpath from the passed context node. */ |
628 | /******************************************************************/ |
629 | xmlNodeSetPtr LIBXMLDOC::GetNodeList(PGLOBAL g, xmlNodePtr np, char *xp) |
630 | { |
631 | xmlNodeSetPtr nl; |
632 | |
633 | if (trace(1)) |
634 | htrc("GetNodeList: %s np=%p\n" , xp, np); |
635 | |
636 | if (!Ctxp) { |
637 | // Init Xpath |
638 | if (trace(1)) |
639 | htrc("Calling xmlPathInit\n" ); |
640 | |
641 | xmlXPathInit(); |
642 | |
643 | if (trace(1)) |
644 | htrc("Calling xmlXPathNewContext Docp=%p\n" , Docp); |
645 | |
646 | // Create xpath evaluation context |
647 | if (!(Ctxp = xmlXPathNewContext(Docp))) { |
648 | strcpy(g->Message, MSG(XPATH_CNTX_ERR)); |
649 | |
650 | if (trace(1)) |
651 | htrc("Context error: %s\n" , g->Message); |
652 | |
653 | return NULL; |
654 | } // endif xpathCtx |
655 | |
656 | // Register namespaces from list (if any) |
657 | for (PNS nsp = Namespaces; nsp; nsp = nsp->Next) { |
658 | if (trace(1)) |
659 | htrc("Calling xmlXPathRegisterNs Prefix=%s Uri=%s\n" , |
660 | nsp->Prefix, nsp->Uri); |
661 | |
662 | if (xmlXPathRegisterNs(Ctxp, BAD_CAST nsp->Prefix, |
663 | BAD_CAST nsp->Uri)) { |
664 | sprintf(g->Message, MSG(REGISTER_ERR), nsp->Prefix, nsp->Uri); |
665 | |
666 | if (trace(1)) |
667 | htrc("Ns error: %s\n" , g->Message); |
668 | |
669 | return NULL; |
670 | } // endif Registering |
671 | |
672 | } // endfor nsp |
673 | |
674 | } // endif Ctxp |
675 | |
676 | if (Xop) { |
677 | if (trace(1)) |
678 | htrc("Calling xmlXPathFreeNodeSetList Xop=%p NOFREE=%d\n" , |
679 | Xop, Nofreelist); |
680 | |
681 | if (Nofreelist) { |
682 | // Making Nlist that must not be freed yet |
683 | // xmlXPathFreeNodeSetList(Xop); // Caused memory leak |
684 | assert(!NlXop); |
685 | NlXop = Xop; // Freed on closing |
686 | Nofreelist = false; |
687 | } else |
688 | xmlXPathFreeObject(Xop); // Caused node not found |
689 | |
690 | if ((Xerr = xmlGetLastError())) { |
691 | strcpy(g->Message, Xerr->message); |
692 | xmlResetError(Xerr); |
693 | return NULL; |
694 | } // endif Xerr |
695 | |
696 | } // endif Xop |
697 | |
698 | // Set the context to the calling node |
699 | Ctxp->node = np; |
700 | |
701 | if (trace(1)) |
702 | htrc("Calling xmlXPathEval %s Ctxp=%p\n" , xp, Ctxp); |
703 | |
704 | // Evaluate table xpath |
705 | if (!(Xop = xmlXPathEval(BAD_CAST xp, Ctxp))) { |
706 | sprintf(g->Message, MSG(XPATH_EVAL_ERR), xp); |
707 | |
708 | if (trace(1)) |
709 | htrc("Path error: %s\n" , g->Message); |
710 | |
711 | return NULL; |
712 | } else |
713 | nl = Xop->nodesetval; |
714 | |
715 | if (trace(1)) |
716 | htrc("GetNodeList nl=%p n=%p\n" , nl, (nl) ? nl->nodeNr : 0); |
717 | |
718 | return nl; |
719 | } // end of GetNodeList |
720 | |
721 | #if 0 // Not used anymore |
722 | /******************************************************************/ |
723 | /* CheckDocument: check if the document is ok to dump. */ |
724 | /* Currently this does the dumping of the document. */ |
725 | /******************************************************************/ |
726 | bool LIBXMLDOC::CheckDocument(FILE *of, xmlNodePtr np) |
727 | { |
728 | int n; |
729 | bool b; |
730 | |
731 | if (!np) |
732 | return true; |
733 | |
734 | if (np->type == XML_ELEMENT_NODE) { |
735 | n = fprintf(of, "<%s" , np->name); |
736 | b = CheckDocument(of, (xmlNodePtr)np->properties); |
737 | |
738 | if (np->children) |
739 | n = fprintf(of, ">" ); |
740 | else |
741 | n = fprintf(of, "/>" ); |
742 | |
743 | } else if (np->type == XML_ATTRIBUTE_NODE) |
744 | n = fprintf(of, " %s=\"" , np->name); |
745 | else if (np->type == XML_TEXT_NODE) |
746 | n = fprintf(of, "%s" , Encode(NULL, (char*)np->content)); |
747 | else if (np->type == XML_COMMENT_NODE) |
748 | n = fprintf(of, "%s" , Encode(NULL, (char*)np->content)); |
749 | |
750 | b = CheckDocument(of, np->children); |
751 | |
752 | if (np->type == XML_ATTRIBUTE_NODE) |
753 | n = fprintf(of, "\"" ); |
754 | else if (!b && np->type == XML_ELEMENT_NODE) |
755 | n = fprintf(of, "</%s>" , np->name); |
756 | |
757 | b = CheckDocument(of, np->next); |
758 | return false; |
759 | } // end of CheckDocument |
760 | |
761 | /******************************************************************/ |
762 | /* Convert node or attribute content to latin characters. */ |
763 | /******************************************************************/ |
764 | int LIBXMLDOC::Decode(xmlChar *cnt, char *buf, int n) |
765 | { |
766 | const char *txt = (const char *)cnt; |
767 | uint dummy_errors; |
768 | uint32 len= copy_and_convert(buf, n, &my_charset_utf8_general_ci, txt, |
769 | strlen(txt), &my_charset_utf8_general_ci, |
770 | &dummy_errors); |
771 | buf[len]= '\0'; |
772 | return 0; |
773 | } // end of Decode |
774 | |
775 | /******************************************************************/ |
776 | /* Convert node or attribute content to latin characters. */ |
777 | /******************************************************************/ |
778 | xmlChar *LIBXMLDOC::Encode(PGLOBAL g, char *txt) |
779 | { |
780 | const CHARSET_INFO *ics= &my_charset_utf8_general_ci; |
781 | const CHARSET_INFO *ocs= &my_charset_utf8_general_ci; |
782 | size_t i = strlen(txt); |
783 | size_t o = i * ocs->mbmaxlen / ics->mbmaxlen + 1; |
784 | char *buf; |
785 | if (g) { |
786 | buf = (char*)PlugSubAlloc(g, NULL, o); |
787 | } else { |
788 | o = 1024; |
789 | buf = Buf; |
790 | } // endif g |
791 | uint dummy_errors; |
792 | uint32 len= copy_and_convert(buf, o, ocs, |
793 | txt, i, ics, |
794 | &dummy_errors); |
795 | buf[len]= '\0'; |
796 | return BAD_CAST buf; |
797 | } // end of Encode |
798 | #endif // 0 |
799 | |
800 | /* ---------------------- class XML2NODE ------------------------ */ |
801 | |
802 | /******************************************************************/ |
803 | /* XML2NODE constructor. */ |
804 | /******************************************************************/ |
805 | XML2NODE::XML2NODE(PXDOC dp, xmlNodePtr np) : XMLNODE(dp) |
806 | { |
807 | Docp = ((PXDOC2)dp)->Docp; |
808 | Content = NULL; |
809 | Nodep = np; |
810 | } // end of XML2NODE constructor |
811 | |
812 | int XML2NODE::GetType(void) |
813 | { |
814 | if (trace(1)) |
815 | htrc("GetType type=%d\n" , Nodep->type); |
816 | |
817 | return Nodep->type; |
818 | } // end of GetType |
819 | |
820 | /******************************************************************/ |
821 | /* Return the node class of next sibling of the node. */ |
822 | /******************************************************************/ |
823 | PXNODE XML2NODE::GetNext(PGLOBAL g) |
824 | { |
825 | if (trace(1)) |
826 | htrc("GetNext\n" ); |
827 | |
828 | if (!Nodep->next) |
829 | Next = NULL; |
830 | else // if (!Next) |
831 | Next = new(g) XML2NODE(Doc, Nodep->next); |
832 | |
833 | return Next; |
834 | } // end of GetNext |
835 | |
836 | /******************************************************************/ |
837 | /* Return the node class of first children of the node. */ |
838 | /******************************************************************/ |
839 | PXNODE XML2NODE::GetChild(PGLOBAL g) |
840 | { |
841 | if (trace(1)) |
842 | htrc("GetChild\n" ); |
843 | |
844 | if (!Nodep->children) |
845 | Children = NULL; |
846 | else // if (!Children) |
847 | Children = new(g) XML2NODE(Doc, Nodep->children); |
848 | |
849 | return Children; |
850 | } // end of GetChild |
851 | |
852 | /******************************************************************/ |
853 | /* Return the content of a node and subnodes. */ |
854 | /******************************************************************/ |
855 | RCODE XML2NODE::GetContent(PGLOBAL g, char *buf, int len) |
856 | { |
857 | RCODE rc = RC_OK; |
858 | |
859 | if (trace(1)) |
860 | htrc("GetContent\n" ); |
861 | |
862 | if (Content) |
863 | xmlFree(Content); |
864 | |
865 | if ((Content = xmlNodeGetContent(Nodep))) { |
866 | char *p1 = (char*)Content, *p2 = buf; |
867 | bool b = false; |
868 | |
869 | // Copy content eliminating extra characters |
870 | for (; *p1; p1++) |
871 | if ((p2 - buf) < len) { |
872 | if (strchr(" \t\r\n" , *p1)) { |
873 | if (b) { |
874 | // This to have one blank between sub-nodes |
875 | *p2++ = ' '; |
876 | b = false; |
877 | } // endif b |
878 | |
879 | } else { |
880 | *p2++ = *p1; |
881 | b = true; |
882 | } // endif p1 |
883 | |
884 | } else { |
885 | sprintf(g->Message, "Truncated %s content" , Nodep->name); |
886 | rc = RC_INFO; |
887 | } // endif len |
888 | |
889 | *p2 = 0; |
890 | |
891 | if (trace(1)) |
892 | htrc("GetText buf='%s' len=%d\n" , buf, len); |
893 | |
894 | xmlFree(Content); |
895 | Content = NULL; |
896 | } else |
897 | *buf = '\0'; |
898 | |
899 | if (trace(1)) |
900 | htrc("GetContent: %s\n" , buf); |
901 | |
902 | return rc; |
903 | } // end of GetContent |
904 | |
905 | /******************************************************************/ |
906 | /* Set the content of a node. */ |
907 | /******************************************************************/ |
908 | bool XML2NODE::SetContent(PGLOBAL g, char *txtp, int len) |
909 | { |
910 | if (trace(1)) |
911 | htrc("SetContent: %s\n" , txtp); |
912 | |
913 | xmlChar *buf = xmlEncodeEntitiesReentrant(Docp, BAD_CAST txtp); |
914 | |
915 | if (trace(1)) |
916 | htrc("SetContent: %s -> %s\n" , txtp, buf); |
917 | |
918 | xmlNodeSetContent(Nodep, buf); |
919 | xmlFree(buf); |
920 | return false; |
921 | } // end of SetContent |
922 | |
923 | /******************************************************************/ |
924 | /* Return a clone of this node. */ |
925 | /******************************************************************/ |
926 | PXNODE XML2NODE::Clone(PGLOBAL g, PXNODE np) |
927 | { |
928 | if (trace(1)) |
929 | htrc("Clone: np=%p\n" , np); |
930 | |
931 | if (np) { |
932 | ((PNODE2)np)->Nodep = Nodep; |
933 | return np; |
934 | } else |
935 | return new(g) XML2NODE(Doc, Nodep); |
936 | |
937 | } // end of Clone |
938 | |
939 | /******************************************************************/ |
940 | /* Return the list of all or matching children that are elements.*/ |
941 | /******************************************************************/ |
942 | PXLIST XML2NODE::GetChildElements(PGLOBAL g, char *xp, PXLIST lp) |
943 | { |
944 | if (trace(1)) |
945 | htrc("GetChildElements: %s\n" , xp); |
946 | |
947 | return SelectNodes(g, (xp) ? xp : (char*)"*" , lp); |
948 | } // end of GetChildElements |
949 | |
950 | /******************************************************************/ |
951 | /* Return the list of nodes verifying the passed Xpath. */ |
952 | /******************************************************************/ |
953 | PXLIST XML2NODE::SelectNodes(PGLOBAL g, char *xp, PXLIST lp) |
954 | { |
955 | if (trace(1)) |
956 | htrc("SelectNodes: %s\n" , xp); |
957 | |
958 | xmlNodeSetPtr nl = ((PXDOC2)Doc)->GetNodeList(g, Nodep, xp); |
959 | |
960 | if (lp) { |
961 | ((PLIST2)lp)->Listp = nl; |
962 | return lp; |
963 | } else |
964 | return new(g) XML2NODELIST(Doc, nl); |
965 | |
966 | } // end of SelectNodes |
967 | |
968 | /******************************************************************/ |
969 | /* Return the first node verifying the passed Xapth. */ |
970 | /******************************************************************/ |
971 | PXNODE XML2NODE::SelectSingleNode(PGLOBAL g, char *xp, PXNODE np) |
972 | { |
973 | if (trace(1)) |
974 | htrc("SelectSingleNode: %s\n" , xp); |
975 | |
976 | xmlNodeSetPtr nl = ((PXDOC2)Doc)->GetNodeList(g, Nodep, xp); |
977 | |
978 | if (nl && nl->nodeNr) { |
979 | if (np) { |
980 | ((PNODE2)np)->Nodep = nl->nodeTab[0]; |
981 | return np; |
982 | } else |
983 | return new(g) XML2NODE(Doc, nl->nodeTab[0]); |
984 | |
985 | } else |
986 | return NULL; |
987 | |
988 | } // end of SelectSingleNode |
989 | |
990 | /******************************************************************/ |
991 | /* Return the node attribute with the specified name. */ |
992 | /******************************************************************/ |
993 | PXATTR XML2NODE::GetAttribute(PGLOBAL g, char *name, PXATTR ap) |
994 | { |
995 | xmlAttrPtr atp; |
996 | |
997 | if (trace(1)) |
998 | htrc("GetAttribute: %s\n" , SVP(name)); |
999 | |
1000 | if (name) |
1001 | atp = xmlHasProp(Nodep, BAD_CAST name); |
1002 | else |
1003 | atp = Nodep->properties; |
1004 | |
1005 | |
1006 | if (atp) { |
1007 | if (ap) { |
1008 | ((PATTR2)ap)->Atrp = atp; |
1009 | ((PATTR2)ap)->Parent = Nodep; |
1010 | return ap; |
1011 | } else |
1012 | return new(g) XML2ATTR(Doc, atp, Nodep); |
1013 | |
1014 | } else |
1015 | return NULL; |
1016 | |
1017 | } // end of GetAttribute |
1018 | |
1019 | /******************************************************************/ |
1020 | /* Add a new child node to this node and return it. */ |
1021 | /******************************************************************/ |
1022 | PXNODE XML2NODE::AddChildNode(PGLOBAL g, PCSZ name, PXNODE np) |
1023 | { |
1024 | char *p, *pn, *pf = NULL, *nmp = PlugDup(g, name); |
1025 | |
1026 | if (trace(1)) |
1027 | htrc("AddChildNode: %s\n" , name); |
1028 | |
1029 | // Is a prefix specified |
1030 | if ((pn = strchr(nmp, ':'))) { |
1031 | pf = nmp; |
1032 | *pn++ = '\0'; // Separate name from prefix |
1033 | } else |
1034 | pn = nmp; |
1035 | |
1036 | // If name has the format m[n] only m is taken as node name |
1037 | if ((p = strchr(pn, '['))) |
1038 | p = BufAlloc(g, pn, p - pn); |
1039 | else |
1040 | p = pn; |
1041 | |
1042 | xmlNodePtr nop = xmlNewChild(Nodep, NULL, BAD_CAST p, NULL); |
1043 | |
1044 | if (!nop) |
1045 | return NULL; |
1046 | |
1047 | if (pf) { |
1048 | // Prefixed name, is it the default NS prefix? |
1049 | if (Doc->DefNs && !strcmp(pf, Doc->DefNs)) |
1050 | pf = NULL; // Default namespace |
1051 | |
1052 | xmlNsPtr nsp = xmlSearchNs(Docp, nop, BAD_CAST pf); |
1053 | |
1054 | if (!nsp) |
1055 | nsp = xmlNewNs(nop, NULL, BAD_CAST pf); |
1056 | |
1057 | // Set node namespace |
1058 | nop->ns = nsp; |
1059 | *(--p) = ':'; // Restore Xname |
1060 | } else if (Doc->DefNs && xmlSearchNs(Docp, nop, NULL)) |
1061 | // Not in default namespace |
1062 | nop->ns = xmlNewNs(nop, BAD_CAST "" , NULL); |
1063 | |
1064 | if (np) |
1065 | ((PNODE2)np)->Nodep = nop; |
1066 | else |
1067 | np = new(g) XML2NODE(Doc, nop); |
1068 | |
1069 | return NewChild(np); |
1070 | } // end of AddChildNode |
1071 | |
1072 | /******************************************************************/ |
1073 | /* Add a new property to this node and return it. */ |
1074 | /******************************************************************/ |
1075 | PXATTR XML2NODE::AddProperty(PGLOBAL g, char *name, PXATTR ap) |
1076 | { |
1077 | if (trace(1)) |
1078 | htrc("AddProperty: %s\n" , name); |
1079 | |
1080 | xmlAttrPtr atp = xmlNewProp(Nodep, BAD_CAST name, NULL); |
1081 | |
1082 | if (atp) { |
1083 | if (ap) { |
1084 | ((PATTR2)ap)->Atrp = atp; |
1085 | ((PATTR2)ap)->Parent = Nodep; |
1086 | return ap; |
1087 | } else |
1088 | return new(g) XML2ATTR(Doc, atp, Nodep); |
1089 | |
1090 | } else |
1091 | return NULL; |
1092 | |
1093 | } // end of AddProperty |
1094 | |
1095 | /******************************************************************/ |
1096 | /* Add a new text node to this node. */ |
1097 | /******************************************************************/ |
1098 | void XML2NODE::AddText(PGLOBAL g, PCSZ txtp) |
1099 | { |
1100 | if (trace(1)) |
1101 | htrc("AddText: %s\n" , txtp); |
1102 | |
1103 | // This is to avoid a blank line when inserting a new line |
1104 | xmlNodePtr np = xmlGetLastChild(Nodep); |
1105 | |
1106 | if (np && np->type == XML_TEXT_NODE) { |
1107 | xmlUnlinkNode(np); |
1108 | xmlFreeNode(np); |
1109 | } // endif type |
1110 | |
1111 | // Add the new text |
1112 | xmlAddChild(Nodep, xmlNewText(BAD_CAST txtp)); |
1113 | } // end of AddText |
1114 | |
1115 | /******************************************************************/ |
1116 | /* Remove a child node from this node. */ |
1117 | /******************************************************************/ |
1118 | void XML2NODE::DeleteChild(PGLOBAL g, PXNODE dnp) |
1119 | { |
1120 | xmlErrorPtr xerr; |
1121 | |
1122 | if (trace(1)) |
1123 | htrc("DeleteChild: node=%p\n" , dnp); |
1124 | |
1125 | xmlNodePtr np = ((PNODE2)dnp)->Nodep; |
1126 | xmlNodePtr text = np->next; |
1127 | |
1128 | // This is specific to row nodes |
1129 | if (text && text->type == XML_TEXT_NODE) { |
1130 | xmlUnlinkNode(text); |
1131 | |
1132 | if ((xerr = xmlGetLastError())) |
1133 | goto err; |
1134 | |
1135 | xmlFreeNode(text); |
1136 | |
1137 | if ((xerr = xmlGetLastError())) |
1138 | goto err; |
1139 | |
1140 | } // endif type |
1141 | |
1142 | xmlUnlinkNode(np); |
1143 | |
1144 | if ((xerr = xmlGetLastError())) |
1145 | goto err; |
1146 | |
1147 | xmlFreeNode(np); |
1148 | |
1149 | if ((xerr = xmlGetLastError())) |
1150 | goto err; |
1151 | |
1152 | Delete(dnp); |
1153 | |
1154 | if ((xerr = xmlGetLastError())) |
1155 | goto err; |
1156 | |
1157 | return; |
1158 | |
1159 | err: |
1160 | if (trace(1)) |
1161 | htrc("DeleteChild: errmsg=%s\n" , xerr->message); |
1162 | |
1163 | xmlResetError(xerr); |
1164 | } // end of DeleteChild |
1165 | |
1166 | /* -------------------- class XML2NODELIST ---------------------- */ |
1167 | |
1168 | /******************************************************************/ |
1169 | /* XML2NODELIST constructor. */ |
1170 | /******************************************************************/ |
1171 | XML2NODELIST::XML2NODELIST(PXDOC dp, xmlNodeSetPtr lp) |
1172 | : XMLNODELIST(dp) |
1173 | { |
1174 | Listp = lp; |
1175 | } // end of XML2NODELIST constructor |
1176 | |
1177 | /******************************************************************/ |
1178 | /* Return the length of the list. */ |
1179 | /******************************************************************/ |
1180 | int XML2NODELIST::GetLength(void) |
1181 | { |
1182 | return (Listp) ? Listp->nodeNr : 0; |
1183 | } // end of GetLength |
1184 | |
1185 | /******************************************************************/ |
1186 | /* Return the nth element of the list. */ |
1187 | /******************************************************************/ |
1188 | PXNODE XML2NODELIST::GetItem(PGLOBAL g, int n, PXNODE np) |
1189 | { |
1190 | if (trace(1)) |
1191 | htrc("GetItem: %d\n" , n); |
1192 | |
1193 | if (!Listp || Listp->nodeNr <= n) |
1194 | return NULL; |
1195 | |
1196 | if (np) { |
1197 | ((PNODE2)np)->Nodep = Listp->nodeTab[n]; |
1198 | return np; |
1199 | } else |
1200 | return new(g) XML2NODE(Doc, Listp->nodeTab[n]); |
1201 | |
1202 | } // end of GetItem |
1203 | |
1204 | /******************************************************************/ |
1205 | /* Reset the pointer on the deleted item. */ |
1206 | /******************************************************************/ |
1207 | bool XML2NODELIST::DropItem(PGLOBAL g, int n) |
1208 | { |
1209 | if (trace(1)) |
1210 | htrc("DropItem: n=%d\n" , n); |
1211 | |
1212 | // We should do something here |
1213 | if (!Listp || Listp->nodeNr <= n) |
1214 | return true; |
1215 | |
1216 | Listp->nodeTab[n] = NULL; // This was causing Valgrind warning |
1217 | return false; |
1218 | } // end of DropItem |
1219 | |
1220 | /* ---------------------- class XML2ATTR ------------------------ */ |
1221 | |
1222 | /******************************************************************/ |
1223 | /* XML2ATTR constructor. */ |
1224 | /******************************************************************/ |
1225 | XML2ATTR::XML2ATTR(PXDOC dp, xmlAttrPtr ap, xmlNodePtr np) |
1226 | : XMLATTRIBUTE(dp) |
1227 | { |
1228 | Atrp = ap; |
1229 | Parent = np; |
1230 | } // end of XML2ATTR constructor |
1231 | |
1232 | /******************************************************************/ |
1233 | /* Return the next sibling of the attribute. */ |
1234 | /******************************************************************/ |
1235 | PXATTR XML2ATTR::GetNext(PGLOBAL g) |
1236 | { |
1237 | if (trace(1)) |
1238 | htrc("Attr GetNext\n" ); |
1239 | |
1240 | if (!Atrp->next) |
1241 | return NULL; |
1242 | else |
1243 | return new(g) XML2ATTR(Doc, Atrp->next, Atrp->parent); |
1244 | |
1245 | } // end of GetNext |
1246 | |
1247 | /******************************************************************/ |
1248 | /* Return the text of an attribute. */ |
1249 | /******************************************************************/ |
1250 | RCODE XML2ATTR::GetText(PGLOBAL g, char *buf, int len) |
1251 | { |
1252 | RCODE rc = RC_OK; |
1253 | xmlChar *txt; |
1254 | |
1255 | if (trace(1)) |
1256 | htrc("GetText\n" ); |
1257 | |
1258 | if ((txt = xmlGetProp(Atrp->parent, Atrp->name))) { |
1259 | // Copy the text to the buffer |
1260 | if (strlen((char*)txt) >= (unsigned)len) { |
1261 | memcpy(buf, txt, len - 1); |
1262 | buf[len - 1] = 0; |
1263 | sprintf(g->Message, "Truncated %s content" , Atrp->name); |
1264 | rc = RC_INFO; |
1265 | } else |
1266 | strcpy(buf, (const char*)txt); |
1267 | |
1268 | xmlFree(txt); |
1269 | } else |
1270 | *buf = '\0'; |
1271 | |
1272 | if (trace(1)) |
1273 | htrc("GetText: %s\n" , buf); |
1274 | |
1275 | return rc; |
1276 | } // end of GetText |
1277 | |
1278 | /******************************************************************/ |
1279 | /* Set the content of an attribute. */ |
1280 | /******************************************************************/ |
1281 | bool XML2ATTR::SetText(PGLOBAL g, char *txtp, int len) |
1282 | { |
1283 | if (trace(1)) |
1284 | htrc("SetText: %s %d\n" , txtp, len); |
1285 | |
1286 | xmlSetProp(Parent, Atrp->name, BAD_CAST txtp); |
1287 | return false; |
1288 | } // end of SetText |
1289 | |