Instrument Neutral Distributed Interface INDI 2.2.5
lilxml.cpp
Go to the documentation of this file.
1#if 0
2liblilxml
3Copyright (C) 2003 Elwood C. Downey
4
5This library is free software;
6you can redistribute it and / or
7modify it under the terms of the GNU Lesser General Public
8License as published by the Free Software Foundation;
9either
10version 2.1 of the License, or (at your option) any later version.
11
12This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY;
14without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16Lesser General Public License for more details.
17
18You should have received a copy of the GNU Lesser General Public
19License along with this library;
20if not, write to the Free Software
21Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110 - 1301 USA
22
23#endif
24
25/* little DOM-style XML parser.
26 * only handles elements, attributes and pcdata content.
27 * <! ... > and <? ... > are silently ignored.
28 * pcdata is collected into one string, sans leading whitespace first line.
29 *
30 * #define MAIN_TST to create standalone test program
31 */
32
33#include <ctype.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37#include <assert.h>
38
39#if defined(_MSC_VER)
40#define snprintf _snprintf
41#pragma warning(push)
43#pragma warning(disable : 4996)
44#endif
45
46#include "lilxml.h"
47
48/* used to efficiently manage growing malloced string space */
49typedef struct
50{
51 char *s; /* malloced memory for string */
52 int sl; /* string length, sans trailing \0 */
53 int sm; /* total malloced bytes */
54} String;
55#define MINMEM 64 /* starting string length */
56
57static int oneXMLchar(LilXML *lp, int c, char ynot[]);
58static void initParser(LilXML *lp);
59static void pushXMLEle(LilXML *lp);
60static void popXMLEle(LilXML *lp);
61static void resetEndTag(LilXML *lp);
62static XMLAtt *growAtt(XMLEle *e);
63static XMLEle *growEle(XMLEle *pe);
64static void freeAtt(XMLAtt *a);
65static int isTokenChar(int start, int c);
66static void growString(String *sp, int c);
67static void appendString(String *sp, const char *str);
68static void freeString(String *sp);
69static void newString(String *sp);
70static void *moremem(void *old, size_t n);
71static void appXMLEle(XMLEle *ep, XMLEle *newep);
72
73typedef enum
74{
75 LOOK4START = 0, /* looking for first element start */
76 LOOK4TAG, /* looking for element tag */
77 INTAG, /* reading tag */
78 LOOK4ATTRN, /* looking for attr name, > or / */
79 INATTRN, /* reading attr name */
80 LOOK4ATTRV, /* looking for attr value */
81 SAWSLASH, /* saw / in element opening */
82 INATTRV, /* in attr value */
83 ENTINATTRV, /* in entity in attr value */
84 LOOK4CON, /* skipping leading content whitespc */
85 INCON, /* reading content */
86 ENTINCON, /* in entity in pcdata */
87 SAWLTINCON, /* saw < in content */
88 LOOK4CLOSETAG, /* looking for closing tag after < */
89 INCLOSETAG /* reading closing tag */
90} State; /* parsing states */
91
92/* maintain state while parsing */
93struct LilXML_
94{
95 State cs; /* current state */
96 int ln; /* line number for diags */
97 XMLEle *ce; /* current element being built */
98 String endtag; /* to check for match with opening tag*/
99 String entity; /* collect entity seq */
100 int delim; /* attribute value delimiter */
101 int lastc; /* last char (just used with skipping)*/
102 int skipping; /* in comment or declaration */
103 int inblob; /* in oneBLOB element */
104};
105
106/* internal representation of a (possibly nested) XML element */
108{
109 String tag; /* element tag */
110 XMLEle *pe; /* parent element, or NULL if root */
111 XMLAtt **at; /* list of attributes */
112 int nat; /* number of attributes */
113 int ait; /* used to iterate over at[] */
114 XMLEle **el; /* list of child elements */
115 int nel; /* number of child elements */
116 int eit; /* used to iterate over el[] */
117 String pcdata; /* character data in this element */
118 int pcdata_hasent; /* 1 if pcdata contains an entity char*/
119};
120
121/* internal representation of an attribute */
123{
124 String name; /* name */
125 String valu; /* value */
126 XMLEle *ce; /* containing element */
127};
128
129/* characters that need escaping as "entities" in attr values and pcdata
130 */
131static char entities[] = "&<>'\"";
132
133/* default memory managers, override with lilxmlMalloc() */
134static void *(*mymalloc)(size_t size) = malloc;
135static void *(*myrealloc)(void *ptr, size_t size) = realloc;
136static void (*myfree)(void *ptr) = free;
137
138/* install new version of malloc/realloc/free.
139 * N.B. don't call after first use of any other lilxml function
140 */
141void lilxmlMalloc(void *(*newmalloc)(size_t size), void *(*newrealloc)(void *ptr, size_t size),
142 void (*newfree)(void *ptr))
143{
144 mymalloc = newmalloc;
145 myrealloc = newrealloc;
146 myfree = newfree;
147}
148
149/* pass back a fresh handle for use with our other functions */
151{
152 LilXML *lp = (LilXML *)moremem(NULL, sizeof * lp);
153 memset(lp, 0, sizeof * lp);
154 initParser(lp);
155 return (lp);
156}
157
158/* discard */
160{
161 delXMLEle(lp->ce);
162 freeString(&lp->endtag);
163 (*myfree)(lp);
164}
165
166/* delete ep and all its children and remove from parent's list if known */
168{
169 int i;
170
171 /* benign if NULL */
172 if (!ep)
173 return;
174
175 /* delete all parts of ep */
176 freeString(&ep->tag);
177 freeString(&ep->pcdata);
178 if (ep->at)
179 {
180 for (i = 0; i < ep->nat; i++)
181 freeAtt(ep->at[i]);
182 (*myfree)(ep->at);
183 }
184 if (ep->el)
185 {
186 for (i = 0; i < ep->nel; i++)
187 {
188 /* forget parent so deleting doesn't modify _this_ el[] */
189 ep->el[i]->pe = NULL;
190
191 delXMLEle(ep->el[i]);
192 }
193 (*myfree)(ep->el);
194 }
195
196 /* remove from parent's list if known */
197 if (ep->pe)
198 {
199 XMLEle *pe = ep->pe;
200 for (i = 0; i < pe->nel; i++)
201 {
202 if (pe->el[i] == ep)
203 {
204 memmove(&pe->el[i], &pe->el[i + 1], (--pe->nel - i) * sizeof(XMLEle *));
205 break;
206 }
207 }
208 }
209
210 /* delete ep itself */
211 (*myfree)(ep);
212}
213
214//#define WITH_MEMCHR
215XMLEle **parseXMLChunk(LilXML *lp, char *buf, int size, char ynot[])
216{
217 unsigned int nnodes = 1;
218 XMLEle **nodes = (XMLEle **)malloc(nnodes * sizeof * nodes);
219 *nodes = NULL;
220 char *curr = buf;
221 int s;
222 ynot[0] = '\0';
223
224 if (lp->inblob)
225 {
226#ifdef WITH_ENCLEN
227 if (size < lp->ce->pcdata.sm - lp->ce->pcdata.sl)
228 {
229 memcpy((void *)(lp->ce->pcdata.s + lp->ce->pcdata.sl), (const void *)buf, size);
230 lp->ce->pcdata.sl += size;
231 return nodes;
232 }
233 else
234 lp->inblob = 0;
235#endif
236#ifdef WITH_MEMCHR
237 char *ltpos = memchr(buf, '<', size);
238 if (!ltpos)
239 {
240 lp->ce->pcdata.s = (char *)moremem(lp->ce->pcdata.s, lp->ce->pcdata.sm + size);
241 lp->ce->pcdata.sm += size;
242 memcpy((void *)(lp->ce->pcdata.s + lp->ce->pcdata.sl), (const void *)buf, size);
243 lp->ce->pcdata.sl += size;
244 return nodes;
245 }
246 else
247 lp->inblob = 0;
248#endif
249 }
250 else
251 {
252 if (lp->ce)
253 {
254 char *ctag = tagXMLEle(lp->ce);
255 if (ctag && !(strcmp(ctag, "oneBLOB")) && (lp->cs == INCON))
256 {
257#ifdef WITH_ENCLEN
258 XMLAtt *blenatt = findXMLAtt(lp->ce, "enclen");
259 if (blenatt)
260 {
261 int blen;
262 sscanf(valuXMLAtt(blenatt), "%d", &blen);
263
264 // Add room for those '\n' on every 72 character line + extra half-full line.
265 blen += (blen / 72) + 1;
266
267 lp->ce->pcdata.s = (char *)moremem(lp->ce->pcdata.s, blen);
268 lp->ce->pcdata.sm = blen; // always set sm
269
270 if (size <= blen - lp->ce->pcdata.sl)
271 {
272 memcpy((void *)(lp->ce->pcdata.s + lp->ce->pcdata.sl), (const void *)buf, size);
273 lp->ce->pcdata.sl += size;
274 lp->inblob = 1;
275 return nodes;
276 }
277 }
278#endif
279#ifdef WITH_MEMCHR
280 char *ltpos = memchr(buf, '<', size);
281 if (!ltpos)
282 {
283 lp->ce->pcdata.s = (char *)moremem(lp->ce->pcdata.s, lp->ce->pcdata.sm + size);
284 lp->ce->pcdata.sm += size;
285 memcpy((void *)(lp->ce->pcdata.s + lp->ce->pcdata.sl), (const void *)buf, size);
286 lp->ce->pcdata.sl += size;
287 lp->inblob = 1;
288 return nodes;
289 }
290 else
291 lp->inblob = 0;
292#endif
293 }
294 }
295 }
296 while (curr - buf < size)
297 {
298 char newc = *curr;
299 /* EOF? */
300 if (newc == 0)
301 {
302 snprintf(ynot, XML_ERROR_SIZE, "Line %d: early XML EOF", lp->ln);
303 initParser(lp);
304 curr++;
305 continue;
306 }
307
308 /* new line? */
309 if (newc == '\n')
310 lp->ln++;
311
312 /* skip comments and declarations. requires 1 char history */
313 if (!lp->skipping && lp->lastc == '<' && (newc == '?' || newc == '!'))
314 {
315 lp->skipping = 1;
316 lp->lastc = newc;
317 curr++;
318 continue;
319 }
320 if (lp->skipping)
321 {
322 if (newc == '>')
323 lp->skipping = 0;
324 lp->lastc = newc;
325 curr++;
326 continue;
327 }
328 if (newc == '<')
329 {
330 lp->lastc = '<';
331 curr++;
332 continue;
333 }
334
335 /* do a pending '<' first then newc */
336 if (lp->lastc == '<')
337 {
338 if (oneXMLchar(lp, '<', ynot) < 0)
339 {
340 initParser(lp);
341 curr++;
342 continue;
343 }
344 /* N.B. we assume '<' will never result in closure */
345 }
346
347 /* process newc (at last!) */
348 s = oneXMLchar(lp, newc, ynot);
349 if (s == 0)
350 {
351 lp->lastc = newc;
352 curr++;
353 continue;
354 }
355 if (s < 0)
356 {
357 initParser(lp);
358 curr++;
359 continue;
360 }
361
362 /* Ok! store ce in nodes and we start over.
363 * N.B. up to caller to call delXMLEle with what we return.
364 */
365 nodes[nnodes - 1] = lp->ce;
366 nodes = (XMLEle **)realloc(nodes, (nnodes + 1) * sizeof * nodes);
367 nodes[nnodes] = NULL;
368 nnodes += 1;
369 lp->ce = NULL;
370 initParser(lp);
371 curr++;
372 }
373 /*
374 * N.B. up to caller to free nodes.
375 */
376 return nodes;
377}
378
379/* process one more character of an XML file.
380 * when find closure with outer element return root of complete tree.
381 * when find error return NULL with reason in ynot[].
382 * when need more return NULL with ynot[0] = '\0'.
383 * N.B. it is up to the caller to delete any tree returned with delXMLEle().
384 */
385XMLEle *readXMLEle(LilXML *lp, int newc, char ynot[])
386{
387 XMLEle *root;
388 int s;
389
390 /* start optimistic */
391 ynot[0] = '\0';
392
393 /* EOF? */
394 if (newc == 0)
395 {
396 snprintf(ynot, XML_ERROR_SIZE, "Line %d: early XML EOF", lp->ln);
397 initParser(lp);
398 return (NULL);
399 }
400
401 /* new line? */
402 if (newc == '\n')
403 lp->ln++;
404
405 /* skip comments and declarations. requires 1 char history */
406 if (!lp->skipping && lp->lastc == '<' && (newc == '?' || newc == '!'))
407 {
408 lp->skipping = 1;
409 lp->lastc = newc;
410 return (NULL);
411 }
412 if (lp->skipping)
413 {
414 if (newc == '>')
415 lp->skipping = 0;
416 lp->lastc = newc;
417 return (NULL);
418 }
419 if (newc == '<')
420 {
421 lp->lastc = '<';
422 return (NULL);
423 }
424
425 /* do a pending '<' first then newc */
426 if (lp->lastc == '<')
427 {
428 if (oneXMLchar(lp, '<', ynot) < 0)
429 {
430 initParser(lp);
431 return (NULL);
432 }
433 /* N.B. we assume '<' will never result in closure */
434 }
435
436 /* process newc (at last!) */
437 s = oneXMLchar(lp, newc, ynot);
438 if (s == 0)
439 {
440 lp->lastc = newc;
441 return (NULL);
442 }
443 if (s < 0)
444 {
445 initParser(lp);
446 return (NULL);
447 }
448
449 /* Ok! return ce and we start over.
450 * N.B. up to caller to call delXMLEle with what we return.
451 */
452 root = lp->ce;
453 lp->ce = NULL;
454 initParser(lp);
455 return (root);
456}
457
458/* parse the given XML string.
459 * return XMLEle* else NULL with reason why in ynot[]
460 */
461XMLEle *parseXML(char buf[], char ynot[])
462{
463 LilXML *lp = newLilXML();
464 XMLEle *root;
465
466 do
467 {
468 root = readXMLEle(lp, *buf++, ynot);
469 }
470 while (!root && !ynot[0]);
471
472 delLilXML(lp);
473
474 return (root);
475}
476
477/* return a deep copy of the given XMLEle *
478 */
480{
481 char *buf;
482 char ynot[XML_ERROR_SIZE];
483 XMLEle *newep;
484
485 buf = (char*)(*mymalloc)(sprlXMLEle(ep, 0) + 1);
486 sprXMLEle(buf, ep, 0);
487 newep = parseXML(buf, ynot);
488 (*myfree)(buf);
489
490 return (newep);
491}
492
493XMLEle * cloneXMLEle(XMLEle * ep, int (*replace)(void * self, XMLEle * source, XMLEle * * replace), void * self)
494{
495 XMLEle * result = nullptr;
496 if (replace && (*replace)(self, ep, &result))
497 {
498 return result;
499 }
500 result = shallowCloneXMLEle(ep);
501
502 for(int i = 0; i < ep->nel; ++i)
503 {
504 XMLEle * child = ep->el[i];
505 XMLEle * repl = cloneXMLEle(child, replace, self);
506 if (repl != nullptr)
507 {
508 repl->pe = result;
509 appXMLEle(result, repl);
510 }
511 }
512
513 if (pcdatalenXMLEle(ep))
514 {
515 editXMLEle(result, pcdataXMLEle(ep));
516 }
517
518 return result;
519}
520
521/* search ep for an attribute with given name.
522 * return NULL if not found.
523 */
524XMLAtt *findXMLAtt(XMLEle *ep, const char *name)
525{
526 int i;
527
528 for (i = 0; i < ep->nat; i++)
529 if (!strcmp(ep->at[i]->name.s, name))
530 return (ep->at[i]);
531 return (NULL);
532}
533
534/* search ep for an element with given tag.
535 * return NULL if not found.
536 */
537XMLEle *findXMLEle(XMLEle *ep, const char *tag)
538{
539 int tl = (int)strlen(tag);
540 int i;
541
542 for (i = 0; i < ep->nel; i++)
543 {
544 String *sp = &ep->el[i]->tag;
545 if (sp->sl == tl && !strcmp(sp->s, tag))
546 return (ep->el[i]);
547 }
548 return (NULL);
549}
550
551/* iterate over each child element of ep.
552 * call first time with first set to 1, then 0 from then on.
553 * returns NULL when no more or err
554 */
556{
557 int eit;
558
559 if (init)
560 ep->eit = 0;
561
562 eit = ep->eit++;
563 if (eit < 0 || eit >= ep->nel)
564 return (NULL);
565 return (ep->el[eit]);
566}
567
568/* iterate over each attribute of ep.
569 * call first time with first set to 1, then 0 from then on.
570 * returns NULL when no more or err
571 */
573{
574 int ait;
575
576 if (init)
577 ep->ait = 0;
578
579 ait = ep->ait++;
580 if (ait < 0 || ait >= ep->nat)
581 return (NULL);
582 return (ep->at[ait]);
583}
584
585/* return parent of given XMLEle */
587{
588 return (ep->pe);
589}
590
591/* return parent element of given XMLAtt */
593{
594 return (ap->ce);
595}
596
597/* access functions */
598
599/* return the tag name of the given element */
601{
602 return (ep->tag.s);
603}
604
605/* return the pcdata portion of the given element */
607{
608 return (ep->pcdata.s);
609}
610
611/* return the number of characters in the pcdata portion of the given element */
613{
614 return (ep->pcdata.sl);
615}
616
617/* return the name of the given attribute */
619{
620 return (ap->name.s);
621}
622
623/* return the value of the given attribute */
625{
626 return (ap->valu.s);
627}
628
629/* return the number of child elements of the given element */
631{
632 return (ep->nel);
633}
634
635/* return the number of attributes in the given element */
637{
638 return (ep->nat);
639}
640
641/* search ep for an attribute with the given name and return its value.
642 * return "" if not found.
643 */
644const char *findXMLAttValu(XMLEle *ep, const char *name)
645{
646 XMLAtt *a = findXMLAtt(ep, name);
647 return (a ? a->valu.s : "");
648}
649
650/* handy wrapper to read one xml file.
651 * return root element else NULL with report in ynot[]
652 */
653XMLEle *readXMLFile(FILE *fp, LilXML *lp, char ynot[])
654{
655 int c;
656
657 while ((c = fgetc(fp)) != EOF)
658 {
659 XMLEle *root = readXMLEle(lp, c, ynot);
660 if (root || ynot[0])
661 return (root);
662 }
663
664 return (NULL);
665}
666
667/* add an element with the given tag to the given element.
668 * parent can be NULL to make a new root.
669 */
670XMLEle *addXMLEle(XMLEle *parent, const char *tag)
671{
672 XMLEle *ep = growEle(parent);
673 appendString(&ep->tag, tag);
674 return (ep);
675}
676
677/* append an existing element to the given element.
678 * N.B. be mindful of when these are deleted, this is not a deep copy.
679 */
680static void appXMLEle(XMLEle *ep, XMLEle *newep)
681{
682 ep->el = (XMLEle **)moremem(ep->el, (ep->nel + 1) * sizeof(XMLEle *));
683 ep->el[ep->nel++] = newep;
684}
685
686/* Update the tag of an element
687 */
688XMLEle *setXMLEleTag(XMLEle *ep, const char * tag)
689{
690 freeString(&ep->tag);
691 newString(&ep->tag);
692 appendString(&ep->tag, tag);
693 return ep;
694}
695
696
697/* set the pcdata of the given element */
698void editXMLEle(XMLEle *ep, const char *pcdata)
699{
700 freeString(&ep->pcdata);
701 appendString(&ep->pcdata, pcdata);
702 ep->pcdata_hasent = (strpbrk(pcdata, entities) != NULL);
703}
704
705/* add an attribute to the given XML element */
706XMLAtt *addXMLAtt(XMLEle *ep, const char *name, const char *valu)
707{
708 XMLAtt *ap = growAtt(ep);
709 appendString(&ap->name, name);
710 appendString(&ap->valu, valu);
711 return (ap);
712}
713
714/* remove the named attribute from ep, if any */
715void rmXMLAtt(XMLEle *ep, const char *name)
716{
717 int i;
718
719 for (i = 0; i < ep->nat; i++)
720 {
721 if (strcmp(ep->at[i]->name.s, name) == 0)
722 {
723 freeAtt(ep->at[i]);
724 memmove(&ep->at[i], &ep->at[i + 1], (--ep->nat - i) * sizeof(XMLAtt *));
725 return;
726 }
727 }
728}
729
730/* Copy a node, without any child (nor cdata) */
732{
733 XMLEle *repl = addXMLEle(nullptr, tagXMLEle(ele));
734
735 for(int i = 0; i < ele->nat; ++i)
736 {
737 auto att = ele->at[i];
738 addXMLAtt(repl, nameXMLAtt(att), valuXMLAtt(att));
739 }
740
741 return repl;
742}
743
744/* change the value of an attribute to str */
745void editXMLAtt(XMLAtt *ap, const char *str)
746{
747 freeString(&ap->valu);
748 appendString(&ap->valu, str);
749}
750
751#define PRINDENT 4 /* sample print indent each level */
752#define PRINDENTSTR " " /* sample print indent each level */
753
754/* Abstract class for XML to string convertion */
756{
757 protected:
759 virtual ~XMLOutput() {};
760
761 protected:
762 virtual void cdataCb(XMLEle * ele)
763 {
764 (void)ele;
765 };
766 public:
767 virtual void put(const char * str, size_t len) = 0;
768 void put(const char * str)
769 {
770 put(str, strlen(str));
771 };
772 void indent(int indent)
773 {
774 for(int i = 0 ; i < indent; ++i) put(PRINDENTSTR, PRINDENT);
775 }
776 void putEntityXML(const char * str);
777
778 /* output a XML node */
779 void putXML(XMLEle * el, int level);
780};
781
782void XMLOutput::putXML(XMLEle *ep, int level)
783{
784 int i;
785 indent(level);
786 put("<");
787 put(ep->tag.s);
788
789 for (i = 0; i < ep->nat; i++)
790 {
791 put(" ");
792 put(ep->at[i]->name.s);
793 put("=\"");
794 putEntityXML(ep->at[i]->valu.s);
795 put("\"");
796 }
797
798 if (ep->nel > 0)
799 {
800 put(">\n");
801 for (i = 0; i < ep->nel; i++)
802 putXML( ep->el[i], level + 1);
803 }
804 if (ep->pcdata.sl > 0)
805 {
806 if (ep->nel == 0)
807 put(">\n");
808 // Declare the cdata offset
809 cdataCb(ep);
810 if (ep->pcdata_hasent)
811 putEntityXML(ep->pcdata.s);
812 else
813 put(ep->pcdata.s);
814 if (ep->pcdata.s[ep->pcdata.sl - 1] != '\n')
815 put("\n");
816 }
817 if (ep->nel > 0 || ep->pcdata.sl > 0)
818 {
819 indent(level);
820 put("</");
821 put(ep->tag.s);
822 put(">\n");
823 }
824 else
825 put("/>\n");
826}
827
828
830{
831 FILE * file;
832 public:
833 FileXMLOutput(FILE * f) : XMLOutput(), file(f) {};
834 virtual ~FileXMLOutput() {};
835 virtual void put(const char * str, size_t len)
836 {
837 fwrite(str, len, 1, file);
838 }
839};
840
841/* sample print ep to fp
842 * N.B. set level = 0 on first call
843 */
844void prXMLEle(FILE *fp, XMLEle *ep, int level)
845{
846 FileXMLOutput fpo(fp);
847 fpo.putXML(ep, level);
848}
849
850/* XML Output to a memory buffer */
852{
853 char * buffer;
854 size_t offset;
855 public:
856 BufferXMLOutput(char * buffer) : XMLOutput(), buffer(buffer), offset(0) {};
857 virtual ~BufferXMLOutput() {};
858 virtual void put(const char * str, size_t len)
859 {
860 memcpy(buffer + offset, str, len);
861 offset += len;
862 }
863 size_t size()
864 {
865 return offset;
866 }
867};
868
869/* sample print ep to string s.
870 * N.B. s must be at least as large as that reported by sprlXMLEle()+1.
871 * N.B. set level = 0 on first call
872 * return length of resulting string (sans trailing \0)
873 */
874size_t sprXMLEle(char *s, XMLEle *ep, int level)
875{
876 BufferXMLOutput bxo(s);
877 bxo.putXML(ep, level);
878 return bxo.size();
879}
880
881/* An output that just count chars (for sizing/locating elements) */
883{
884 size_t offset;
885 XMLEle * cdataWatch;
886 size_t cdataOffset;
887 protected:
888 virtual void cdataCb(XMLEle * ele)
889 {
890 if (ele == cdataWatch && cdataWatch)
891 {
892 cdataOffset = offset;
893 }
894 }
895 public:
896 NullXMLOutput() : XMLOutput(), offset(0), cdataWatch(nullptr), cdataOffset((size_t) -1) {};
897 virtual ~NullXMLOutput() {};
898 virtual void put(const char * str, size_t len)
899 {
900 (void)str;
901 offset += len;
902 }
903 size_t size()
904 {
905 return offset;
906 }
907
909 {
910 cdataWatch = ele;
911 }
912 size_t cdataFound()
913 {
914 return cdataOffset;
915 }
916};
917
918/* return number of bytes in a string guaranteed able to hold result of
919 * sprXLMEle(ep) (sans trailing \0).
920 * N.B. set level = 0 on first call
921 */
922size_t sprlXMLEle(XMLEle *ep, int level)
923{
924 NullXMLOutput bxo;
925 bxo.putXML(ep, level);
926 return bxo.size();
927}
928
929/* Return the exact starting offset of the CDATA of ep in the printed representation */
930size_t sprXMLCDataOffset(XMLEle * root, XMLEle * ep, int level)
931{
932 NullXMLOutput bxo;
933 bxo.setCdataWatch(ep);
934 bxo.putXML(root, level);
935 return bxo.cdataFound();
936}
937
938void XMLOutput::putEntityXML(const char * s)
939{
940 const char *ep = NULL;
941 for (; (ep = strpbrk(s, entities)) != NULL; s = ep + 1)
942 {
943 /* found another entity, copy preceding to malloced buffer */
944 size_t nnew = size_t(ep - s); /* all but entity itself */
945 put(s, nnew);
946
947 /* replace with entity encoding */
948 switch (*ep)
949 {
950 case '&':
951 put("&amp;");
952 break;
953 case '<':
954 put("&lt;");
955 break;
956 case '>':
957 put("&gt;");
958 break;
959 case '\'':
960 put("&apos;");
961 break;
962 case '"':
963 put("&quot;");
964 break;
965 }
966 }
967
968 put(s);
969}
970
971/* return a string with all xml-sensitive characters within the passed string s
972 * replaced with their entity sequence equivalents.
973 * N.B. caller must use the returned string before calling us again.
974 */
975char *entityXML(char *s)
976{
977 // FIXME: this is not thread safe. Signature must be changed (deprecate ?)
978 static char *malbuf;
979 size_t nmalbuf = 0;
980 char *sret = NULL;
981 char *ep = NULL;
982
983 /* scan for each entity, if any */
984 for (sret = s; (ep = strpbrk(s, entities)) != NULL; s = ep + 1)
985 {
986 /* found another entity, copy preceding to malloced buffer */
987 size_t nnew = size_t(ep - s); /* all but entity itself */
988 sret = malbuf = (char*)moremem(malbuf, nmalbuf + nnew + 10);
989 memcpy(malbuf + nmalbuf, s, nnew);
990 nmalbuf += nnew;
991
992 /* replace with entity encoding */
993 switch (*ep)
994 {
995 case '&':
996 nmalbuf += sprintf(malbuf + nmalbuf, "&amp;");
997 break;
998 case '<':
999 nmalbuf += sprintf(malbuf + nmalbuf, "&lt;");
1000 break;
1001 case '>':
1002 nmalbuf += sprintf(malbuf + nmalbuf, "&gt;");
1003 break;
1004 case '\'':
1005 nmalbuf += sprintf(malbuf + nmalbuf, "&apos;");
1006 break;
1007 case '"':
1008 nmalbuf += sprintf(malbuf + nmalbuf, "&quot;");
1009 break;
1010 }
1011 }
1012
1013 /* return s if no entities, else malloc cleaned-up copy */
1014 if (sret == s)
1015 {
1016 /* using s, so free any alloced memory from last time */
1017 if (malbuf)
1018 {
1019 free(malbuf);
1020 malbuf = NULL;
1021 }
1022 return s;
1023 }
1024 else
1025 {
1026 /* put remaining part of s into malbuf */
1027 size_t nleft = strlen(s) + 1; /* include \0 */
1028 sret = malbuf = (char*)moremem(malbuf, nmalbuf + nleft);
1029 memcpy(malbuf + nmalbuf, s, nleft);
1030 }
1031
1032 return (sret);
1033}
1034
1035/* if ent is a recognized xml entity sequence, set *cp to char and return 1
1036 * else return 0
1037 */
1038static int decodeEntity(char *ent, int *cp)
1039{
1040 static struct
1041 {
1042 const char *ent;
1043 char c;
1044 } enttable[] =
1045 {
1046 { "&amp;", '&' }, { "&apos;", '\'' }, { "&lt;", '<' }, { "&gt;", '>' }, { "&quot;", '"' },
1047 };
1048 for (size_t i = 0; i < (sizeof(enttable) / sizeof(enttable[0])); i++)
1049 {
1050 if (strcmp(ent, enttable[i].ent) == 0)
1051 {
1052 *cp = enttable[i].c;
1053 return (1);
1054 }
1055 }
1056
1057 return (0);
1058}
1059
1060/* process one more char in XML file.
1061 * if find final closure, return 1 and tree is in ce.
1062 * if need more, return 0.
1063 * if real trouble, return -1 and put reason in ynot.
1064 */
1065static int oneXMLchar(LilXML *lp, int c, char ynot[])
1066{
1067 switch (lp->cs)
1068 {
1069 case LOOK4START: /* looking for first element start */
1070 if (c == '<')
1071 {
1072 pushXMLEle(lp);
1073 lp->cs = LOOK4TAG;
1074 }
1075 /* silently ignore until resync */
1076 break;
1077
1078 case LOOK4TAG: /* looking for element tag */
1079 if (isTokenChar(1, c))
1080 {
1081 growString(&lp->ce->tag, c);
1082 lp->cs = INTAG;
1083 }
1084 else if (!isspace(c))
1085 {
1086 snprintf(ynot, XML_ERROR_SIZE, "Line %d: Bogus tag char %c", lp->ln, c);
1087 return (-1);
1088 }
1089 break;
1090
1091 case INTAG: /* reading tag */
1092 if (isTokenChar(0, c))
1093 growString(&lp->ce->tag, c);
1094 else if (c == '>')
1095 lp->cs = LOOK4CON;
1096 else if (c == '/')
1097 lp->cs = SAWSLASH;
1098 else
1099 lp->cs = LOOK4ATTRN;
1100 break;
1101
1102 case LOOK4ATTRN: /* looking for attr name, > or / */
1103 if (c == '>')
1104 lp->cs = LOOK4CON;
1105 else if (c == '/')
1106 lp->cs = SAWSLASH;
1107 else if (isTokenChar(1, c))
1108 {
1109 XMLAtt *ap = growAtt(lp->ce);
1110 growString(&ap->name, c);
1111 lp->cs = INATTRN;
1112 }
1113 else if (!isspace(c))
1114 {
1115 snprintf(ynot, XML_ERROR_SIZE, "Line %d: Bogus leading attr name char: %c", lp->ln, c);
1116 return (-1);
1117 }
1118 break;
1119
1120 case SAWSLASH: /* saw / in element opening */
1121 if (c == '>')
1122 {
1123 if (!lp->ce->pe)
1124 return (1); /* root has no content */
1125 popXMLEle(lp);
1126 lp->cs = LOOK4CON;
1127 }
1128 else
1129 {
1130 snprintf(ynot, XML_ERROR_SIZE, "Line %d: Bogus char %c before >", lp->ln, c);
1131 return (-1);
1132 }
1133 break;
1134
1135 case INATTRN: /* reading attr name */
1136 if (isTokenChar(0, c))
1137 growString(&lp->ce->at[lp->ce->nat - 1]->name, c);
1138 else if (isspace(c) || c == '=')
1139 lp->cs = LOOK4ATTRV;
1140 else
1141 {
1142 snprintf(ynot, XML_ERROR_SIZE, "Line %d: Bogus attr name char: %c", lp->ln, c);
1143 return (-1);
1144 }
1145 break;
1146
1147 case LOOK4ATTRV: /* looking for attr value */
1148 if (c == '\'' || c == '"')
1149 {
1150 lp->delim = c;
1151 lp->cs = INATTRV;
1152 }
1153 else if (!(isspace(c) || c == '='))
1154 {
1155 snprintf(ynot, XML_ERROR_SIZE, "Line %d: No value for attribute %s", lp->ln, lp->ce->at[lp->ce->nat - 1]->name.s);
1156 return (-1);
1157 }
1158 break;
1159
1160 case INATTRV: /* in attr value */
1161 if (c == '&')
1162 {
1163 newString(&lp->entity);
1164 growString(&lp->entity, c);
1165 lp->cs = ENTINATTRV;
1166 }
1167 else if (c == lp->delim)
1168 lp->cs = LOOK4ATTRN;
1169 else if (!iscntrl(c))
1170 growString(&lp->ce->at[lp->ce->nat - 1]->valu, c);
1171 break;
1172
1173 case ENTINATTRV: /* working on entity in attr valu */
1174 if (c == ';')
1175 {
1176 /* if find a recognized esp seq, add equiv char else raw seq */
1177 growString(&lp->entity, c);
1178 if (decodeEntity(lp->entity.s, &c))
1179 growString(&lp->ce->at[lp->ce->nat - 1]->valu, c);
1180 else
1181 appendString(&lp->ce->at[lp->ce->nat - 1]->valu, lp->entity.s);
1182 freeString(&lp->entity);
1183 lp->cs = INATTRV;
1184 }
1185 else
1186 growString(&lp->entity, c);
1187 break;
1188
1189 case LOOK4CON: /* skipping leading content whitespace*/
1190 if (c == '<')
1191 lp->cs = SAWLTINCON;
1192 else if (!isspace(c))
1193 {
1194 growString(&lp->ce->pcdata, c);
1195 lp->cs = INCON;
1196 }
1197 break;
1198
1199 case INCON: /* reading content */
1200 if (c == '&')
1201 {
1202 newString(&lp->entity);
1203 growString(&lp->entity, c);
1204 lp->cs = ENTINCON;
1205 }
1206 else if (c == '<')
1207 {
1208 /* chomp trailing whitespace */
1209 while (lp->ce->pcdata.sl > 0 && isspace(lp->ce->pcdata.s[lp->ce->pcdata.sl - 1]))
1210 lp->ce->pcdata.s[--(lp->ce->pcdata.sl)] = '\0';
1211 lp->cs = SAWLTINCON;
1212 }
1213 else
1214 {
1215 growString(&lp->ce->pcdata, c);
1216 }
1217 break;
1218
1219 case ENTINCON: /* working on entity in content */
1220 if (c == ';')
1221 {
1222 /* if find a recognized esc seq, add equiv char else raw seq */
1223 growString(&lp->entity, c);
1224 if (decodeEntity(lp->entity.s, &c))
1225 growString(&lp->ce->pcdata, c);
1226 else
1227 {
1228 appendString(&lp->ce->pcdata, lp->entity.s);
1229 //lp->ce->pcdata_hasent = 1;
1230 }
1231 // JM 2018-09-26: Even if decoded, we always set
1232 // pcdata_hasent to 1 since we need to encode it again
1233 // before sending it over to clients and drivers.
1234 lp->ce->pcdata_hasent = 1;
1235 freeString(&lp->entity);
1236 lp->cs = INCON;
1237 }
1238 else
1239 growString(&lp->entity, c);
1240 break;
1241
1242 case SAWLTINCON: /* saw < in content */
1243 if (c == '/')
1244 {
1245 resetEndTag(lp);
1246 lp->cs = LOOK4CLOSETAG;
1247 }
1248 else
1249 {
1250 pushXMLEle(lp);
1251 if (isTokenChar(1, c))
1252 {
1253 growString(&lp->ce->tag, c);
1254 lp->cs = INTAG;
1255 }
1256 else
1257 lp->cs = LOOK4TAG;
1258 }
1259 break;
1260
1261 case LOOK4CLOSETAG: /* looking for closing tag after < */
1262 if (isTokenChar(1, c))
1263 {
1264 growString(&lp->endtag, c);
1265 lp->cs = INCLOSETAG;
1266 }
1267 else if (!isspace(c))
1268 {
1269 snprintf(ynot, XML_ERROR_SIZE, "Line %d: Bogus preend tag char %c", lp->ln, c);
1270 return (-1);
1271 }
1272 break;
1273
1274 case INCLOSETAG: /* reading closing tag */
1275 if (isTokenChar(0, c))
1276 growString(&lp->endtag, c);
1277 else if (c == '>')
1278 {
1279 if (strcmp(lp->ce->tag.s, lp->endtag.s))
1280 {
1281 snprintf(ynot, XML_ERROR_SIZE, "Line %d: closing tag %s does not match %s", lp->ln, lp->endtag.s, lp->ce->tag.s);
1282 return (-1);
1283 }
1284 else if (lp->ce->pe)
1285 {
1286 popXMLEle(lp);
1287 lp->cs = LOOK4CON; /* back to content after nested elem */
1288 }
1289 else
1290 return (1); /* yes! */
1291 }
1292 else if (!isspace(c))
1293 {
1294 snprintf(ynot, XML_ERROR_SIZE, "Line %d: Bogus end tag char %c", lp->ln, c);
1295 return (-1);
1296 }
1297 break;
1298 }
1299
1300 return (0);
1301}
1302
1303/* set up for a fresh start again */
1304static void initParser(LilXML *lp)
1305{
1306 delXMLEle(lp->ce);
1307 freeString(&lp->endtag);
1308 memset(lp, 0, sizeof(*lp));
1309 newString(&lp->endtag);
1310 lp->cs = LOOK4START;
1311 lp->ln = 1;
1312}
1313
1314/* start a new XMLEle.
1315 * point ce to a new XMLEle.
1316 * if ce already set up, add to its list of child elements too.
1317 * endtag no longer valid.
1318 */
1319static void pushXMLEle(LilXML *lp)
1320{
1321 lp->ce = growEle(lp->ce);
1322 resetEndTag(lp);
1323}
1324
1325/* point ce to parent of current ce.
1326 * endtag no longer valid.
1327 */
1328static void popXMLEle(LilXML *lp)
1329{
1330 lp->ce = lp->ce->pe;
1331 resetEndTag(lp);
1332}
1333
1334/* return one new XMLEle, added to the given element if given */
1335static XMLEle *growEle(XMLEle *pe)
1336{
1337 XMLEle *newe = (XMLEle *)moremem(NULL, sizeof(XMLEle));
1338
1339 memset(newe, 0, sizeof(XMLEle));
1340 newString(&newe->tag);
1341 newString(&newe->pcdata);
1342 newe->pe = pe;
1343
1344 if (pe)
1345 {
1346 pe->el = (XMLEle **)moremem(pe->el, (pe->nel + 1) * sizeof(XMLEle *));
1347 pe->el[pe->nel++] = newe;
1348 }
1349
1350 return (newe);
1351}
1352
1353/* add room for and return one new XMLAtt to the given element */
1354static XMLAtt *growAtt(XMLEle *ep)
1355{
1356 XMLAtt *newa = (XMLAtt *)moremem(NULL, sizeof * newa);
1357
1358 memset(newa, 0, sizeof(*newa));
1359 newString(&newa->name);
1360 newString(&newa->valu);
1361 newa->ce = ep;
1362
1363 ep->at = (XMLAtt **)moremem(ep->at, (ep->nat + 1) * sizeof(XMLAtt *));
1364 ep->at[ep->nat++] = newa;
1365
1366 return (newa);
1367}
1368
1369/* free a and all it holds */
1370static void freeAtt(XMLAtt *a)
1371{
1372 if (!a)
1373 return;
1374 freeString(&a->name);
1375 freeString(&a->valu);
1376 (*myfree)(a);
1377}
1378
1379/* reset endtag */
1380static void resetEndTag(LilXML *lp)
1381{
1382 freeString(&lp->endtag);
1383 newString(&lp->endtag);
1384}
1385
1386/* 1 if c is a valid token character, else 0.
1387 * it can be alpha or '_' or numeric unless start.
1388 */
1389static int isTokenChar(int start, int c)
1390{
1391 return (isalpha(c) || c == '_' || (!start && isdigit(c)));
1392}
1393
1394/* grow the String storage at *sp to append c */
1395static void growString(String *sp, int c)
1396{
1397 int l = sp->sl + 2; /* need room for '\0' plus c */
1398
1399 if (l > sp->sm)
1400 {
1401 if (!sp->s)
1402 newString(sp);
1403 else
1404 {
1405 sp->s = (char *)moremem(sp->s, sp->sm *= 2);
1406 }
1407 }
1408 sp->s[--l] = '\0';
1409 sp->s[--l] = (char)c;
1410 sp->sl++;
1411}
1412
1413/* append str to the String storage at *sp */
1414static void appendString(String *sp, const char *str)
1415{
1416 if (!sp || !str)
1417 return;
1418
1419 int strl = int(strlen(str));
1420 int l = sp->sl + strl + 1; /* need room for '\0' */
1421
1422 if (l > sp->sm)
1423 {
1424 if (!sp->s)
1425 newString(sp);
1426 if (l > sp->sm)
1427 {
1428 sp->s = (char *)moremem(sp->s, (sp->sm = l));
1429 }
1430 }
1431 if (sp->s)
1432 {
1433 strcpy(&sp->s[sp->sl], str);
1434 sp->sl += strl;
1435 }
1436}
1437
1438/* init a String with a malloced string containing just \0 */
1439static void newString(String *sp)
1440{
1441 if (!sp)
1442 return;
1443
1444 sp->s = (char *)moremem(NULL, MINMEM);
1445 sp->sm = MINMEM;
1446 *sp->s = '\0';
1447 sp->sl = 0;
1448}
1449
1450/* free memory used by the given String */
1451static void freeString(String *sp)
1452{
1453 if (sp->s)
1454 (*myfree)(sp->s);
1455 sp->s = NULL;
1456 sp->sl = 0;
1457 sp->sm = 0;
1458}
1459
1460/* like malloc but knows to use realloc if already started */
1461static void *moremem(void *old, size_t n)
1462{
1463 void *p = (old ? (*myrealloc)(old, n) : (*mymalloc)(n));
1464 if (p == 0)
1465 {
1466 fprintf(stderr, "%s(%s): Failed to allocate memory.\n", __FILE__, __func__);
1467 exit(1);
1468 }
1469 return p;
1470}
1471
1472#if defined(MAIN_TST)
1473int main(int ac, char *av[])
1474{
1475 LilXML *lp = newLilXML();
1476 char ynot[XML_ERROR_SIZE];
1477 XMLEle *root;
1478
1479 root = readXMLFile(stdin, lp, ynot);
1480 if (root)
1481 {
1482 char *str;
1483 int l;
1484
1485 if (ac > 1)
1486 {
1487 XMLEle *theend = addXMLEle(root, "theend");
1488 editXMLEle(theend, "Added to test editing");
1489 addXMLAtt(theend, "hello", "world");
1490 }
1491
1492 fprintf(stderr, "::::::::::::: %s\n", tagXMLEle(root));
1493 prXMLEle(stdout, root, 0);
1494
1495 l = sprlXMLEle(root, 0);
1496 str = malloc(l + 1);
1497 fprintf(stderr, "::::::::::::: %s : %d : %d", tagXMLEle(root), l, sprXMLEle(str, root, 0));
1498 fprintf(stderr, ": %d\n", printf("%s", str));
1499
1500 delXMLEle(root);
1501 }
1502 else if (ynot[0])
1503 {
1504 fprintf(stderr, "Error: %s\n", ynot);
1505 }
1506
1507 delLilXML(lp);
1508
1509 return (0);
1510}
1511#endif
1512
1513#if defined(_MSC_VER)
1514#undef snprintf
1515#pragma warning(pop)
1516#endif
virtual ~BufferXMLOutput()
Definition lilxml.cpp:857
size_t size()
Definition lilxml.cpp:863
BufferXMLOutput(char *buffer)
Definition lilxml.cpp:856
virtual void put(const char *str, size_t len)
Definition lilxml.cpp:858
virtual ~FileXMLOutput()
Definition lilxml.cpp:834
virtual void put(const char *str, size_t len)
Definition lilxml.cpp:835
FileXMLOutput(FILE *f)
Definition lilxml.cpp:833
size_t size()
Definition lilxml.cpp:903
virtual void put(const char *str, size_t len)
Definition lilxml.cpp:898
virtual ~NullXMLOutput()
Definition lilxml.cpp:897
void setCdataWatch(XMLEle *ele)
Definition lilxml.cpp:908
virtual void cdataCb(XMLEle *ele)
Definition lilxml.cpp:888
size_t cdataFound()
Definition lilxml.cpp:912
void indent(int indent)
Definition lilxml.cpp:772
virtual void cdataCb(XMLEle *ele)
Definition lilxml.cpp:762
virtual void put(const char *str, size_t len)=0
virtual ~XMLOutput()
Definition lilxml.cpp:759
void put(const char *str)
Definition lilxml.cpp:768
void putXML(XMLEle *el, int level)
Definition lilxml.cpp:782
void putEntityXML(const char *str)
Definition lilxml.cpp:938
int main()
const char * findXMLAttValu(XMLEle *ep, const char *name)
Find an XML element's attribute value.
Definition lilxml.cpp:644
XMLEle * nextXMLEle(XMLEle *ep, int init)
Iterate an XML element for a list of nesetd XML elements.
Definition lilxml.cpp:555
XMLEle * parentXMLEle(XMLEle *ep)
Return the parent of an XML element.
Definition lilxml.cpp:586
void lilxmlMalloc(void *(*newmalloc)(size_t size), void *(*newrealloc)(void *ptr, size_t size), void(*newfree)(void *ptr))
Definition lilxml.cpp:141
int nXMLEle(XMLEle *ep)
Return the number of nested XML elements in a parent XML element.
Definition lilxml.cpp:630
XMLAtt * findXMLAtt(XMLEle *ep, const char *name)
Find an XML attribute within an XML element.
Definition lilxml.cpp:524
LilXML * newLilXML()
Create a new lilxml parser.
Definition lilxml.cpp:150
int nXMLAtt(XMLEle *ep)
Return the number of XML attributes in a parent XML element.
Definition lilxml.cpp:636
char * tagXMLEle(XMLEle *ep)
Return the tag of an XML element.
Definition lilxml.cpp:600
char * nameXMLAtt(XMLAtt *ap)
Return the name of an XML attribute.
Definition lilxml.cpp:618
XMLEle * addXMLEle(XMLEle *parent, const char *tag)
add an element with the given tag to the given element. parent can be NULL to make a new root.
Definition lilxml.cpp:670
XMLEle * findXMLEle(XMLEle *ep, const char *tag)
Find an XML element within an XML element.
Definition lilxml.cpp:537
size_t sprXMLEle(char *s, XMLEle *ep, int level)
sample print ep to string s. N.B. s must be at least as large as that reported by sprlXMLEle()+1....
Definition lilxml.cpp:874
State
Definition lilxml.cpp:74
@ ENTINCON
Definition lilxml.cpp:86
@ LOOK4START
Definition lilxml.cpp:75
@ LOOK4TAG
Definition lilxml.cpp:76
@ ENTINATTRV
Definition lilxml.cpp:83
@ INATTRN
Definition lilxml.cpp:79
@ LOOK4CON
Definition lilxml.cpp:84
@ INTAG
Definition lilxml.cpp:77
@ LOOK4CLOSETAG
Definition lilxml.cpp:88
@ SAWLTINCON
Definition lilxml.cpp:87
@ LOOK4ATTRV
Definition lilxml.cpp:80
@ INCON
Definition lilxml.cpp:85
@ SAWSLASH
Definition lilxml.cpp:81
@ INCLOSETAG
Definition lilxml.cpp:89
@ LOOK4ATTRN
Definition lilxml.cpp:78
@ INATTRV
Definition lilxml.cpp:82
#define PRINDENTSTR
Definition lilxml.cpp:752
XMLEle * parentXMLAtt(XMLAtt *ap)
Return the parent of an XML attribute.
Definition lilxml.cpp:592
XMLEle ** parseXMLChunk(LilXML *lp, char *buf, int size, char ynot[])
Process an XML chunk.
Definition lilxml.cpp:215
#define MINMEM
Definition lilxml.cpp:55
char * valuXMLAtt(XMLAtt *ap)
Return the value of an XML attribute.
Definition lilxml.cpp:624
XMLEle * parseXML(char buf[], char ynot[])
Definition lilxml.cpp:461
void editXMLEle(XMLEle *ep, const char *pcdata)
set the pcdata of the given element
Definition lilxml.cpp:698
XMLEle * setXMLEleTag(XMLEle *ep, const char *tag)
Update the tag of an element.
Definition lilxml.cpp:688
size_t sprXMLCDataOffset(XMLEle *root, XMLEle *ep, int level)
return exact position of cdata of child in printed representation of root N.B. set level = 0 on first...
Definition lilxml.cpp:930
void rmXMLAtt(XMLEle *ep, const char *name)
Remove an XML attribute from an XML element.
Definition lilxml.cpp:715
void prXMLEle(FILE *fp, XMLEle *ep, int level)
Print an XML element.
Definition lilxml.cpp:844
size_t sprlXMLEle(XMLEle *ep, int level)
return number of bytes in a string guaranteed able to hold result of sprXLMEle(ep) (sans trailing \0@...
Definition lilxml.cpp:922
XMLEle * readXMLEle(LilXML *lp, int newc, char ynot[])
Process an XML one char at a time.
Definition lilxml.cpp:385
void delXMLEle(XMLEle *ep)
delXMLEle Delete XML element.
Definition lilxml.cpp:167
XMLEle * cloneXMLEle(XMLEle *ep)
Definition lilxml.cpp:479
char * pcdataXMLEle(XMLEle *ep)
Return the pcdata of an XML element.
Definition lilxml.cpp:606
void editXMLAtt(XMLAtt *ap, const char *str)
change the value of an attribute to str.
Definition lilxml.cpp:745
void delLilXML(LilXML *lp)
Delete a lilxml parser.
Definition lilxml.cpp:159
#define PRINDENT
Definition lilxml.cpp:751
XMLAtt * nextXMLAtt(XMLEle *ep, int init)
Iterate an XML element for a list of XML attributes.
Definition lilxml.cpp:572
XMLEle * shallowCloneXMLEle(XMLEle *ele)
return a surface copy of a node. Don't copy childs or cdata.
Definition lilxml.cpp:731
XMLAtt * addXMLAtt(XMLEle *ep, const char *name, const char *valu)
Add an XML attribute to an existing XML element.
Definition lilxml.cpp:706
XMLEle * readXMLFile(FILE *fp, LilXML *lp, char ynot[])
Handy wrapper to read one xml file.
Definition lilxml.cpp:653
char * entityXML(char *s)
return a string with all xml-sensitive characters within the passed string replaced with their entity...
Definition lilxml.cpp:975
int pcdatalenXMLEle(XMLEle *ep)
Return the number of characters in pcdata in an XML element.
Definition lilxml.cpp:612
A little DOM-style library to handle parsing and processing an XML file.
#define XML_ERROR_SIZE
Definition lilxml.h:73
int ln
Definition lilxml.cpp:96
String endtag
Definition lilxml.cpp:98
int skipping
Definition lilxml.cpp:102
int delim
Definition lilxml.cpp:100
int lastc
Definition lilxml.cpp:101
State cs
Definition lilxml.cpp:95
String entity
Definition lilxml.cpp:99
int inblob
Definition lilxml.cpp:103
XMLEle * ce
Definition lilxml.cpp:97
int sm
Definition lilxml.cpp:53
char * s
Definition lilxml.cpp:51
int sl
Definition lilxml.cpp:52
XMLEle * ce
Definition lilxml.cpp:126
String name
Definition lilxml.cpp:124
String valu
Definition lilxml.cpp:125
int ait
Definition lilxml.cpp:113
String tag
Definition lilxml.cpp:109
XMLAtt ** at
Definition lilxml.cpp:111
int eit
Definition lilxml.cpp:116
XMLEle ** el
Definition lilxml.cpp:114
int nat
Definition lilxml.cpp:112
String pcdata
Definition lilxml.cpp:117
int nel
Definition lilxml.cpp:115
int pcdata_hasent
Definition lilxml.cpp:118
XMLEle * pe
Definition lilxml.cpp:110