Instrument Neutral Distributed Interface INDI  2.0.2
lilxml.cpp
Go to the documentation of this file.
1 #if 0
2 liblilxml
3 Copyright (C) 2003 Elwood C. Downey
4 
5 This library is free software;
6 you can redistribute it and / or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation;
9 either
10 version 2.1 of the License, or (at your option) any later version.
11 
12 This library is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY;
14 without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17 
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library;
20 if not, write to the Free Software
21 Foundation, 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 */
49 typedef 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 
57 static int oneXMLchar(LilXML *lp, int c, char ynot[]);
58 static void initParser(LilXML *lp);
59 static void pushXMLEle(LilXML *lp);
60 static void popXMLEle(LilXML *lp);
61 static void resetEndTag(LilXML *lp);
62 static XMLAtt *growAtt(XMLEle *e);
63 static XMLEle *growEle(XMLEle *pe);
64 static void freeAtt(XMLAtt *a);
65 static int isTokenChar(int start, int c);
66 static void growString(String *sp, int c);
67 static void appendString(String *sp, const char *str);
68 static void freeString(String *sp);
69 static void newString(String *sp);
70 static void *moremem(void *old, size_t n);
71 static void appXMLEle(XMLEle *ep, XMLEle *newep);
72 
73 typedef 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 */
93 struct 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 wiht 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 */
107 struct xml_ele_
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 */
122 struct xml_att_
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  */
131 static char entities[] = "&<>'\"";
132 
133 /* default memory managers, override with lilxmlMalloc() */
134 static void *(*mymalloc)(size_t size) = malloc;
135 static void *(*myrealloc)(void *ptr, size_t size) = realloc;
136 static 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  */
141 void 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 */
159 void delLilXML(LilXML *lp)
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 */
167 void delXMLEle(XMLEle *ep)
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
215 XMLEle **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  sprintf(ynot, "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 outter 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  */
385 XMLEle *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  sprintf(ynot, "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  */
461 XMLEle *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[1024];
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 
493 XMLEle * 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  */
524 XMLAtt *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  */
537 XMLEle *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  */
555 XMLEle *nextXMLEle(XMLEle *ep, int init)
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  */
572 XMLAtt *nextXMLAtt(XMLEle *ep, int init)
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 */
600 char *tagXMLEle(XMLEle *ep)
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 */
618 char *nameXMLAtt(XMLAtt *ap)
619 {
620  return (ap->name.s);
621 }
622 
623 /* return the value of the given attribute */
624 char *valuXMLAtt(XMLAtt *ap)
625 {
626  return (ap->valu.s);
627 }
628 
629 /* return the number of child elements of the given element */
630 int nXMLEle(XMLEle *ep)
631 {
632  return (ep->nel);
633 }
634 
635 /* return the number of attributes in the given element */
636 int nXMLAtt(XMLEle *ep)
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  */
644 const 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  */
653 XMLEle *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  */
670 XMLEle *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  */
680 static 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  */
688 XMLEle *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 */
698 void 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 */
706 XMLAtt *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 */
715 void 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 */
745 void 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 
782 void 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  */
844 void 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  */
874 size_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 
908  void setCdataWatch(XMLEle * ele)
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  */
922 size_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 */
930 size_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 
938 void 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  */
975 char *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  */
1038 static 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  */
1065 static 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  sprintf(ynot, "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  sprintf(ynot, "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  sprintf(ynot, "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  sprintf(ynot, "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  sprintf(ynot, "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 recongized 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  sprintf(ynot, "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  sprintf(ynot, "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  sprintf(ynot, "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 */
1304 static 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  */
1319 static 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  */
1328 static 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 */
1335 static 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 */
1354 static 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 */
1370 static 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 */
1380 static 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  */
1389 static 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 */
1395 static 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 */
1414 static 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 */
1439 static 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 */
1451 static 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 */
1461 static 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)
1473 int main(int ac, char *av[])
1474 {
1475  LilXML *lp = newLilXML();
1476  char ynot[1024];
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
XMLOutput()
Definition: lilxml.cpp:758
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
XMLEle * parentXMLAtt(XMLAtt *ap)
Return the parent of an XML attribute.
Definition: lilxml.cpp:592
XMLAtt * nextXMLAtt(XMLEle *ep, int init)
Iterate an XML element for a list of XML attributes.
Definition: lilxml.cpp:572
XMLAtt * findXMLAtt(XMLEle *ep, const char *name)
Find an XML attribute within an XML element.
Definition: lilxml.cpp:524
XMLEle * shallowCloneXMLEle(XMLEle *ele)
return a surface copy of a node. Don't copy childs or cdata.
Definition: lilxml.cpp:731
LilXML * newLilXML()
Create a new lilxml parser.
Definition: lilxml.cpp:150
XMLEle * cloneXMLEle(XMLEle *ep)
Definition: lilxml.cpp:479
char * entityXML(char *s)
return a string with all xml-sensitive characters within the passed string replaced with their entity...
Definition: lilxml.cpp:975
XMLEle * parentXMLEle(XMLEle *ep)
Return the parent of an XML element.
Definition: lilxml.cpp:586
int nXMLAtt(XMLEle *ep)
Return the number of XML attributes in a parent XML element.
Definition: lilxml.cpp:636
XMLEle ** parseXMLChunk(LilXML *lp, char *buf, int size, char ynot[])
Process an XML chunk.
Definition: lilxml.cpp:215
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
const char * findXMLAttValu(XMLEle *ep, const char *name)
Find an XML element's attribute value.
Definition: lilxml.cpp:644
#define MINMEM
Definition: lilxml.cpp:55
XMLAtt * addXMLAtt(XMLEle *ep, const char *name, const char *valu)
Add an XML attribute to an existing XML element.
Definition: lilxml.cpp:706
char * pcdataXMLEle(XMLEle *ep)
Return the pcdata of an XML element.
Definition: lilxml.cpp:606
void editXMLEle(XMLEle *ep, const char *pcdata)
set the pcdata of the given element
Definition: lilxml.cpp:698
char * tagXMLEle(XMLEle *ep)
Return the tag of an XML element.
Definition: lilxml.cpp:600
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 * readXMLFile(FILE *fp, LilXML *lp, char ynot[])
Handy wrapper to read one xml file.
Definition: lilxml.cpp:653
XMLEle * readXMLEle(LilXML *lp, int newc, char ynot[])
Process an XML one char at a time.
Definition: lilxml.cpp:385
XMLEle * nextXMLEle(XMLEle *ep, int init)
Iterate an XML element for a list of nesetd XML elements.
Definition: lilxml.cpp:555
char * nameXMLAtt(XMLAtt *ap)
Return the name of an XML attribute.
Definition: lilxml.cpp:618
void delXMLEle(XMLEle *ep)
delXMLEle Delete XML element.
Definition: lilxml.cpp:167
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
XMLEle * setXMLEleTag(XMLEle *ep, const char *tag)
Update the tag of an element.
Definition: lilxml.cpp:688
#define PRINDENT
Definition: lilxml.cpp:751
XMLEle * parseXML(char buf[], char ynot[])
Definition: lilxml.cpp:461
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
int pcdatalenXMLEle(XMLEle *ep)
Return the number of characters in pcdata in an XML element.
Definition: lilxml.cpp:612
XMLEle * findXMLEle(XMLEle *ep, const char *tag)
Find an XML element within an XML element.
Definition: lilxml.cpp:537
char * valuXMLAtt(XMLAtt *ap)
Return the value of an XML attribute.
Definition: lilxml.cpp:624
A little DOM-style library to handle parsing and processing an XML file.
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
int main(int, char *[])