diff options
Diffstat (limited to 'kstars/kstars/indi/lilxml.c')
-rw-r--r-- | kstars/kstars/indi/lilxml.c | 762 |
1 files changed, 762 insertions, 0 deletions
diff --git a/kstars/kstars/indi/lilxml.c b/kstars/kstars/indi/lilxml.c new file mode 100644 index 00000000..c8344b5e --- /dev/null +++ b/kstars/kstars/indi/lilxml.c @@ -0,0 +1,762 @@ +#if 0 + liblilxml + Copyright (C) 2003 Elwood C. Downey + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +#endif + +/* little DOM-style XML parser. + * only handles elements, attributes and pcdata content. + * <! ... > and <? ... > are silently ignored. + * pcdata is collected into one string, sans leading whitespace first line. + * + * #define MAIN_TST to create standalone test program + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "lilxml.h" +#include "indicom.h" + +static int oneXMLchar (LilXML *lp, int c, char errmsg[]); +static void initParser(LilXML *lp); +static void pushXMLEle(LilXML *lp); +static void popXMLEle(LilXML *lp); +static void resetEndTag(LilXML *lp); +static void addAttr(LilXML *lp); +static void delAttr (XMLAtt *a); +static int isTokenChar (int start, int c); +static void growString (char **sp, int c); +static void growPCData (XMLEle *ep, int c); +static char *newString (void); +static void *moremem (void *old, int n); +static void freemem (void *m); + +typedef enum { + LOOK4START = 0, /* looking for first element start */ + LOOK4TAG, /* looking for element tag */ + INTAG, /* reading tag */ + LOOK4ATTRN, /* looking for attr name, > or / */ + INATTRN, /* reading attr name */ + LOOK4ATTRV, /* looking for attr value */ + SAWSLASH, /* saw / in element opening */ + INATTRV, /* in attr value */ + LOOK4CON, /* skipping leading content whitespc */ + INCON, /* reading content */ + LTINCON, /* saw < in content */ + LOOK4CLOSETAG, /* looking for closing tag after < */ + INCLOSETAG /* reading closing tag */ +} State; /* parsing states */ + +/* maintain state while parsing */ +struct _LilXML { + State cs; /* current state */ + int ln; /* line number for diags */ + XMLEle *ce; /* current element being built */ + char *endtag; /* to check for match with opening tag*/ + int delim; /* attribute value delimiter */ + int lastc; /* last char (just used wiht skipping)*/ + int skipping; /* in comment or declaration */ +}; + +/* internal representation of a (possibly nested) XML element */ +struct _xml_ele { + char *tag; /* element tag */ + struct _xml_ele *pe; /* parent element, or NULL if root */ + XMLAtt **at; /* list of attributes */ + int nat; /* number of attributes */ + int ait; /* used to iterate over at[] */ + struct _xml_ele **el; /* list of child elements */ + int nel; /* number of child elements */ + int eit; /* used to iterate over el[] */ + char *pcdata; /* character data in this element */ + int pcdatal; /* handy length sans \0 (tends to be big) */ +}; + +/* internal representation of an attribute */ +struct _xml_att { + char *name; /* name */ + char *valu; /* value */ + struct _xml_ele *ce; /* containing element */ +}; + +/* default memory managers, override with indi_xmlMalloc() */ +static void *(*mymalloc)(size_t size) = malloc; +static void *(*myrealloc)(void *ptr, size_t size) = realloc; +static void (*myfree)(void *ptr) = free; + +/* install new version of malloc/realloc/free. + * N.B. don't call after first use of any other lilxml function + */ +void +indi_xmlMalloc (void *(*newmalloc)(size_t size), + void *(*newrealloc)(void *ptr, size_t size), + void (*newfree)(void *ptr)) +{ + mymalloc = newmalloc; + myrealloc = newrealloc; + myfree = newfree; +} + +/* pass back a fresh handle for use with our other functions */ +LilXML * +newLilXML () +{ + LilXML *lp = (LilXML *) moremem (NULL, sizeof(LilXML)); + initParser(lp); + return (lp); +} + +/* discard */ +void +delLilXML (LilXML *lp) +{ + freemem (lp); +} + +/* delete ep and all its children */ +void +delXMLEle (XMLEle *ep) +{ + int i; + + /* benign if NULL */ + if (!ep) + return; + + /* delete all parts of ep */ + freemem (ep->tag); + freemem (ep->pcdata); + if (ep->at) { + for (i = 0; i < ep->nat; i++) + delAttr (ep->at[i]); + freemem (ep->at); + } + if (ep->el) { + for (i = 0; i < ep->nel; i++) + delXMLEle (ep->el[i]); + freemem (ep->el); + } + + /* delete ep itself */ + freemem (ep); +} + +/* process one more character of an XML file. + * when find closure with outter element return root of complete tree. + * when find error return NULL with reason in errmsg[]. + * when need more return NULL with errmsg[0] = '\0'. + * N.B. it is up to the caller to delete the tree delXMLEle(). + */ +XMLEle * +readXMLEle (LilXML *lp, int newc, char errmsg[]) +{ + XMLEle *root; + int s; + + /* start optimistic */ + errmsg[0] = '\0'; + + /* EOF? */ + if (newc == 0) { + snprintf (errmsg, ERRMSG_SIZE, "Line %d: XML EOF", lp->ln); + initParser(lp); + return (NULL); + } + + /* new line? */ + if (newc == '\n') + lp->ln++; + + /* skip comments and declarations. requires 1 char history */ + if (!lp->skipping && lp->lastc == '<' && (newc == '?' || newc == '!')) { + lp->skipping = 1; + lp->lastc = newc; + return (NULL); + } + if (lp->skipping) { + if (newc == '>') + lp->skipping = 0; + lp->lastc = newc; + return (NULL); + } + if (newc == '<') { + lp->lastc = '<'; + return (NULL); + } + + /* do a pending '<' first then newc */ + if (lp->lastc == '<') { + if (oneXMLchar (lp, '<', errmsg) < 0) { + initParser(lp); + return (NULL); + } + /* N.B. we assume '<' will never result in closure */ + } + + /* process newc (at last!) */ + s = oneXMLchar (lp, newc, errmsg); + if (s == 0) { + lp->lastc = newc; + return (NULL); + } + if (s < 0) { + initParser(lp); + return (NULL); + } + + /* Ok! return ce and we start over. + * N.B. up to caller to call delXMLEle with what we return. + */ + root = lp->ce; + lp->ce = NULL; + initParser(lp); + return (root); +} + +/* search ep for an attribute with given name. + * return NULL if not found. + */ +XMLAtt * +findXMLAtt (XMLEle *ep, const char *name) +{ + int i; + + for (i = 0; i < ep->nat; i++) + if (!strcmp (ep->at[i]->name, name)) + return (ep->at[i]); + return (NULL); +} + +/* search ep for an element with given tag. + * return NULL if not found. + */ +XMLEle * +findXMLEle (XMLEle *ep, const char *tag) +{ + int i; + + for (i = 0; i < ep->nel; i++) + if (!strcmp (ep->el[i]->tag, tag)) + return (ep->el[i]); + return (NULL); +} + +/* iterate over each child element of ep. + * call first time with first set to 1, then 0 from then on. + * returns NULL when no more or err + */ +XMLEle * +nextXMLEle (XMLEle *ep, int init) +{ + int eit; + + if (init) + ep->eit = 0; + + eit = ep->eit++; + if (eit < 0 || eit >= ep->nel) + return (NULL); + return (ep->el[eit]); +} + +/* iterate over each attribute of ep. + * call first time with first set to 1, then 0 from then on. + * returns NULL when no more or err + */ +XMLAtt * +nextXMLAtt (XMLEle *ep, int init) +{ + int ait; + + if (init) + ep->ait = 0; + + ait = ep->ait++; + if (ait < 0 || ait >= ep->nat) + return (NULL); + return (ep->at[ait]); +} + +/* return parent of given XMLEle */ +XMLEle * +parentXMLEle (XMLEle *ep) +{ + return (ep->pe); +} + +/* return parent element of given XMLAtt */ +XMLEle * +parentXMLAtt (XMLAtt *ap) +{ + return (ap->ce); +} + +/* access functions */ + +/* return the tag name of the given element */ +char * +tagXMLEle (XMLEle *ep) +{ + return (ep->tag); +} + +/* return the pcdata portion of the given element */ +char * +pcdataXMLEle (XMLEle *ep) +{ + return (ep->pcdata); +} + +/* return the number of characters in the pcdata portion of the given element */ +int +pcdatalenXMLEle (XMLEle *ep) +{ + return (ep->pcdatal); +} + +/* return the nanme of the given attribute */ +char * +nameXMLAtt (XMLAtt *ap) +{ + return (ap->name); +} + +/* return the value of the given attribute */ +char * +valuXMLAtt (XMLAtt *ap) +{ + return (ap->valu); +} + +/* return the number of child elements of the given element */ +int +nXMLEle (XMLEle *ep) +{ + return (ep->nel); +} + +/* return the number of attributes in the given element */ +int +nXMLAtt (XMLEle *ep) +{ + return (ep->nat); +} + + +/* search ep for an attribute with the given name and return its value. + * return "" if not found. + */ +const char * +findXMLAttValu (XMLEle *ep, char *name) +{ + XMLAtt *a = findXMLAtt (ep, name); + return (a ? a->valu : ""); +} + +/* handy wrapper to read one xml file. + * return root element else NULL with report in errmsg[] + */ +XMLEle * +readXMLFile (FILE *fp, LilXML *lp, char errmsg[]) +{ + int c; + + while ((c = fgetc(fp)) != EOF) { + XMLEle *root = readXMLEle (lp, c, errmsg); + if (root || errmsg[0]) + return (root); + } + + return (NULL); +} + +/* sample print ep to fp + * N.B. set level = 0 on first call + */ +#define PRINDENT 4 /* sample print indent each level */ +void +prXMLEle (FILE *fp, XMLEle *ep, int level) +{ + int indent = level*PRINDENT; + int i; + + fprintf (fp, "%*s<%s", indent, "", ep->tag); + for (i = 0; i < ep->nat; i++) + fprintf (fp, " %s=\"%s\"", ep->at[i]->name, ep->at[i]->valu); + if (ep->nel > 0) { + fprintf (fp, ">\n"); + for (i = 0; i < ep->nel; i++) + prXMLEle (fp, ep->el[i], level+1); + } + if (ep->pcdata[0]) { + char *nl; + if (ep->nel == 0) + fprintf (fp, ">\n"); + /* indent if none or one line */ + nl = strpbrk (ep->pcdata, "\n\r"); + if (!nl || nl == &ep->pcdata[ep->pcdatal-1]) + fprintf (fp, "%*s", indent+PRINDENT, ""); + fprintf (fp, "%s", ep->pcdata); + if (!nl) + fprintf (fp, "\n"); + } + if (ep->nel > 0 || ep->pcdata[0]) + fprintf (fp, "%*s</%s>\n", indent, "", ep->tag); + else + fprintf (fp, "/>\n"); +} + + + +/* process one more char in XML file. + * if find final closure, return 1 and tree is in ce. + * if need more, return 0. + * if real trouble, return -1 and put reason in errmsg. + */ +static int +oneXMLchar (LilXML *lp, int c, char errmsg[]) +{ + switch (lp->cs) { + case LOOK4START: /* looking for first element start */ + if (c == '<') { + pushXMLEle(lp); + lp->cs = LOOK4TAG; + } + /* silently ignore until resync */ + break; + + case LOOK4TAG: /* looking for element tag */ + if (isTokenChar (1, c)) { + growString (&lp->ce->tag, c); + lp->cs = INTAG; + } else if (!isspace(c)) { + snprintf (errmsg, ERRMSG_SIZE, "Line %d: Bogus tag char %c", lp->ln, c); + return (-1); + } + break; + + case INTAG: /* reading tag */ + if (isTokenChar (0, c)) + growString (&lp->ce->tag, c); + else if (c == '>') + lp->cs = LOOK4CON; + else if (c == '/') + lp->cs = SAWSLASH; + else + lp->cs = LOOK4ATTRN; + break; + + case LOOK4ATTRN: /* looking for attr name, > or / */ + if (c == '>') + lp->cs = LOOK4CON; + else if (c == '/') + lp->cs = SAWSLASH; + else if (isTokenChar (1, c)) { + addAttr(lp); + growString (&lp->ce->at[lp->ce->nat-1]->name, c); + lp->cs = INATTRN; + } else if (!isspace(c)) { + snprintf (errmsg, ERRMSG_SIZE, "Line %d: Bogus leading attr name char: %c", + lp->ln, c); + return (-1); + } + break; + + case SAWSLASH: /* saw / in element opening */ + if (c == '>') { + if (!lp->ce->pe) + return(1); /* root has no content */ + popXMLEle(lp); + lp->cs = LOOK4CON; + } else { + snprintf (errmsg, ERRMSG_SIZE, "Line %d: Bogus char %c before >", lp->ln, c); + return (-1); + } + break; + + case INATTRN: /* reading attr name */ + if (isTokenChar (0, c)) + growString (&lp->ce->at[lp->ce->nat-1]->name, c); + else if (isspace(c) || c == '=') + lp->cs = LOOK4ATTRV; + else { + snprintf (errmsg, ERRMSG_SIZE, "Line %d: Bogus attr name char: %c", lp->ln,c); + return (-1); + } + break; + + case LOOK4ATTRV: /* looking for attr value */ + if (c == '\'' || c == '"') { + lp->delim = c; + growString (&lp->ce->at[lp->ce->nat-1]->valu, '\0'); + lp->cs = INATTRV; + } else if (!(isspace(c) || c == '=')) { + snprintf (errmsg, ERRMSG_SIZE, "Line %d: No value for attribute %.100s", lp->ln, + lp->ce->at[lp->ce->nat-1]->name); + return (-1); + } + break; + + case INATTRV: /* in attr value */ + if (c == lp->delim) + lp->cs = LOOK4ATTRN; + else if (!iscntrl(c)) + growString (&lp->ce->at[lp->ce->nat-1]->valu, c); + break; + + case LOOK4CON: /* skipping leading content whitespace*/ + if (c == '<') + lp->cs = LTINCON; + else if (!isspace(c)) { + growPCData (lp->ce, c); + lp->cs = INCON; + } + break; + + case INCON: /* reading content */ + if (c == '<') { + /* if text contains a nl trim trailing blanks. + * chomp trailing nl if only one. + */ + char *nl = strpbrk (lp->ce->pcdata, "\n\r"); + if (nl) + while (lp->ce->pcdatal > 0 && + lp->ce->pcdata[lp->ce->pcdatal-1] == ' ') + lp->ce->pcdata[--lp->ce->pcdatal] = '\0'; + if (nl == &lp->ce->pcdata[lp->ce->pcdatal-1]) + lp->ce->pcdata[--lp->ce->pcdatal] = '\0'; /* safe! */ + lp->cs = LTINCON; + } else + growPCData (lp->ce, c); + break; + + case LTINCON: /* saw < in content */ + if (c == '/') { + resetEndTag(lp); + lp->cs = LOOK4CLOSETAG; + } else { + pushXMLEle(lp); + if (isTokenChar(1,c)) { + growString (&lp->ce->tag, c); + lp->cs = INTAG; + } else + lp->cs = LOOK4TAG; + } + break; + + case LOOK4CLOSETAG: /* looking for closing tag after < */ + if (isTokenChar (1, c)) { + growString (&lp->endtag, c); + lp->cs = INCLOSETAG; + } else if (!isspace(c)) { + snprintf (errmsg, ERRMSG_SIZE, "Line %d: Bogus preend tag char %c", lp->ln,c); + return (-1); + } + break; + + case INCLOSETAG: /* reading closing tag */ + if (isTokenChar(0, c)) + growString (&lp->endtag, c); + else if (c == '>') { + if (strcmp (lp->ce->tag, lp->endtag)) { + snprintf (errmsg, ERRMSG_SIZE, "Line %d: closing tag %.64s does not match %.64s", + lp->ln, lp->endtag, lp->ce->tag); + return (-1); + } else if (lp->ce->pe) { + popXMLEle(lp); + lp->cs = LOOK4CON; /* back to content after nested elem */ + } else + return (1); /* yes! */ + } else if (!isspace(c)) { + snprintf (errmsg, ERRMSG_SIZE, "Line %d: Bogus end tag char %c", lp->ln, c); + return (-1); + } + break; + } + + return (0); +} + +/* set up for a fresh start */ +static void +initParser(LilXML *lp) +{ + memset (lp, 0, sizeof(*lp)); + lp->cs = LOOK4START; + lp->ln = 1; + delXMLEle (lp->ce); + lp->ce = NULL; + resetEndTag(lp); + lp->lastc = 0; + lp->skipping = 0; +} + +/* start a new XMLEle. + * if ce already set up, add to its list of child elements. + * point ce to a new XMLEle. + * endtag no longer valid. + */ +static void +pushXMLEle(LilXML *lp) +{ + XMLEle *newe = (XMLEle *) moremem (NULL, sizeof(XMLEle)); + XMLEle *ce = lp->ce; + + memset (newe, 0, sizeof(*newe)); + newe->tag = newString(); + newe->pcdata = newString(); + newe->pe = ce; + + if (ce) { + ce->el = (XMLEle **) moremem (ce->el, (ce->nel+1)*sizeof(XMLEle*)); + ce->el[ce->nel++] = newe; + } + lp->ce = newe; + resetEndTag(lp); +} + +/* point ce to parent of current ce. + * endtag no longer valid. + */ +static void +popXMLEle(LilXML *lp) +{ + lp->ce = lp->ce->pe; + resetEndTag(lp); +} + +/* add one new XMLAtt to the current element */ +static void +addAttr(LilXML *lp) +{ + XMLAtt *newa = (XMLAtt *) moremem (NULL, sizeof(XMLAtt)); + XMLEle *ce = lp->ce; + + memset (newa, 0, sizeof(*newa)); + newa->name = newString(); + newa->valu = newString(); + newa->ce = ce; + + ce->at = (XMLAtt **) moremem (ce->at, (ce->nat+1)*sizeof(XMLAtt *)); + ce->at[ce->nat++] = newa; +} + +/* delete a and all it holds */ +static void +delAttr (XMLAtt *a) +{ + if (!a) + return; + if (a->name) + freemem (a->name); + if (a->valu) + freemem (a->valu); + freemem(a); +} + +/* delete endtag if appropriate */ +static void +resetEndTag(LilXML *lp) +{ + if (lp->endtag) { + freemem (lp->endtag); + lp->endtag = 0; + } +} + +/* 1 if c is a valid token character, else 0. + * it can be alpha or '_' or numeric unless start. + */ +static int +isTokenChar (int start, int c) +{ + return (isalpha(c) || c == '_' || (!start && isdigit(c))); +} + +/* grow the malloced string at *sp to append c */ +static void +growString (char **sp, int c) +{ + int l = *sp ? strlen(*sp) : 0; + *sp = (char *) moremem (*sp, l+2); /* c + '\0' */ + (*sp)[l++] = (char)c; + (*sp)[l] = '\0'; +} + +/* special fast version of growString just for ep->pcdata that avoids all the + * strlens and tiny increments in allocated mem + */ +static void +growPCData (XMLEle *ep, int c) +{ + int l = ep->pcdatal++; + if ((l%32) == 0) { + int nm = 32*(l/32+1) + 2; /* c + '\0' */ + ep->pcdata = (char *) moremem (ep->pcdata, nm); + } + ep->pcdata[l++] = (char)c; + ep->pcdata[l] = '\0'; +} + +/* return a malloced string of one '\0' */ +static char * +newString() +{ + char *str; + + *(str = (char *)moremem(NULL, 16)) = '\0'; /* expect more */ + return (str); +} + +static void * +moremem (void *old, int n) +{ + return (old ? realloc (old, n) : malloc (n)); +} + +static void +freemem (void *m) +{ + free (m); +} + +#if defined(MAIN_TST) +int +main (int ac, char *av[]) +{ + LilXML *lp = newLilXML(); + char errmsg[ERRMSG_SIZE]; + XMLEle *root; + + root = readXMLFile (stdin, lp, errmsg); + if (root) { + fprintf (stderr, "::::::::::::: %s\n", tagXMLEle(root)); + prXMLEle (stdout, root, 0); + delXMLEle (root); + } else if (errmsg[0]) { + fprintf (stderr, "Error: %s\n", errmsg); + } + + delLilXML (lp); + + return (0); +} +#endif + |