# HG changeset patch # User asl # Date 1107130170 0 # Node ID 6036f158c5ff1c0ceee9136b81df799c8bb78123 # Parent a16252f31c445b41d8e0e8b74ec3e21cd9b11628 * Import of the ezxml XML parsing library diff --git a/packages/services/ezxml/current/ChangeLog b/packages/services/ezxml/current/ChangeLog new file mode 100644 --- /dev/null +++ b/packages/services/ezxml/current/ChangeLog @@ -0,0 +1,35 @@ +2005-01-31 Andrew Lunn + + * Import of the ezxml XML parsing library + +//=========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2005 Andrew Lunn +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos 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 General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//=========================================================================== diff --git a/packages/services/ezxml/current/cdl/ezxml.cdl b/packages/services/ezxml/current/cdl/ezxml.cdl new file mode 100644 --- /dev/null +++ b/packages/services/ezxml/current/cdl/ezxml.cdl @@ -0,0 +1,80 @@ +# ==================================================================== +# +# ezxml.cdl +# +# XML parser +# +# ==================================================================== +#####ECOSGPLCOPYRIGHTBEGIN#### +## ------------------------------------------- +## This file is part of eCos, the Embedded Configurable Operating System. +## Copyright (C) 2005 eCosCentric Ltd +## +## eCos is free software; you can redistribute it and/or modify it under +## the terms of the GNU General Public License as published by the Free +## Software Foundation; either version 2 or (at your option) any later version. +## +## eCos 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 General Public License +## for more details. +## +## You should have received a copy of the GNU General Public License along +## with eCos; if not, write to the Free Software Foundation, Inc., +## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +## +## As a special exception, if other files instantiate templates or use macros +## or inline functions from this file, or you compile this file and link it +## with other works to produce a work based on this file, this file does not +## by itself cause the resulting work to be covered by the GNU General Public +## License. However the source code for this file must still be made available +## in accordance with section (3) of the GNU General Public License. +## +## This exception does not invalidate any other reasons why a work based on +## this file might be covered by the GNU General Public License. +## +## Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +## at http://sources.redhat.com/ecos/ecos-license/ +## ------------------------------------------- +#####ECOSGPLCOPYRIGHTEND#### +# ==================================================================== +######DESCRIPTIONBEGIN#### +# +# Author(s): Matt Jerdonek +# Original data: Matt Jerdonek +# Contributors: +# Date: 2005-01-31 +# +#####DESCRIPTIONEND#### +# +# ==================================================================== + +cdl_package CYGPKG_EZXML { + display "ezXML XML parser" + description " + ezXML is a C library for parsing XML documents inspired by + simpleXML for PHP. As the name implies, it's easy to + use. It's ideal for parsing xml configuration files or + REST web service responses. It's also fast and lightweight + (11k compiled)." + + compile ezxml.c + + cdl_option CYGPKG_EZXML_CFLAGS_ADD { + display "Additional compiler flags" + flavor data + no_define + default_value { "-D__ECOS__" } + description " + This option modifies the set of compiler flags for + building this package. These flags are used in addition + to the set of global flags." + } + + cdl_option CYGPKG_EZXML_TESTS { + display "ezXML tests" + flavor data + no_define + calculated { "tests/ezxml" } + } +} diff --git a/packages/services/ezxml/current/doc/ezxml.html b/packages/services/ezxml/current/doc/ezxml.html new file mode 100644 --- /dev/null +++ b/packages/services/ezxml/current/doc/ezxml.html @@ -0,0 +1,121 @@ + + + ezXML + +

ezXML - XML Parsing C Library

+

version 0.8

+

+ ezXML is a C library for parsing XML documents inspired by + simpleXML for + PHP. As the name implies, it's easy to use. It's ideal for parsing xml + configuration files or REST web service responses. It's also fast and + lightweight (11k compiled). The latest version is available here: + ezxml-0.8.tar.gz +

+ + Example Usage +

+ Given the following example xml document: +

+ + <?xml version="1.0"?>
+ <formula1>
+   <team name="McLaren">
+     <driver>
+       <name>Kimi + Raikkonen</name>
+       <points>45</points>
+     </driver>
+     <driver>
+       <name>David + Coultard</name>
+       <points>24</points>
+     </driver>
+   </team>
+ </formula1> +
+

+ This code snipped prints out a list of drivers, which team they drive for, + and how many championship points they have: +

+ + ezxml_t f1 = ezxml_parse_file("formula1.xml"), team, driver;
+ const char *teamname;
+  
+ for (team = ezxml_child(f1, "team"); team; team = team->next) {
+     teamname = ezxml_attr(team, "name");
+     for (driver = ezxml_child(team, "driver"); driver; + driver = driver->next) {
+         printf("%s, %s: %s\n", + ezxml_child(driver, "name")->txt, teamname,
+              +   ezxml_child(driver, "points")->txt);
+     }
+ }
+ ezxml_free(f1); +
+

+ Alternately, the following would print out the name of the second driver + of the first team: +

+ + ezxml_t f1 = ezxml_parse_file("formula1.xml");
+  
+ printf("%s\n", ezxml_get(f1, "team", 0, "driver", 1, "name", -1)->txt); +
ezxml_free(f1); +
+

+ The -1 indicates the end of the argument list. That's pretty much all + there is to it. Complete API documentation can be found in ezxml.h. +

+ + Known Limitations + + + Licensing +

+ ezXML was written by Aaron Voisine and is distributed under the terms of + the MIT license. +

+ + diff --git a/packages/services/ezxml/current/doc/ezxml.txt b/packages/services/ezxml/current/doc/ezxml.txt new file mode 100644 --- /dev/null +++ b/packages/services/ezxml/current/doc/ezxml.txt @@ -0,0 +1,84 @@ +ezXML - XML Parsing C Library +version 0.8 + +ezXML is a C library for parsing XML documents inspired by simpleXML for PHP. +As the name implies, it's easy to use. It's ideal for parsing xml configuration +files or REST web service responses. It's also fast and lightweight (11k +compiled). The latest version is available here: +http://prdownloads.sf.net/ezxml/ezxml-0.8.tar.gz?download + +Example Usage + +Given the following example xml document: + + + + + + Kimi Raikkonen + 45 + + + David Coultard + 24 + + + + +This code snipped prints out a list of drivers, which team they drive for, +and how many championship points they have: + +ezxml_t f1 = ezxml_parse_file("formula1.xml"), team, driver; +const char *teamname; + +for (team = ezxml_child(f1, "team"); team; team = team->next) { + teamname = ezxml_attr(team, "name"); + for (driver = ezxml_child(team, "driver"); driver; driver = driver->next) { + printf("%s, %s: %s\n", ezxml_child(driver, "name")->txt, teamname, + ezxml_child(driver, "points")->txt); + } +} +ezxml_free(f1); + +Alternately, the following would print out the name of the second driver of the +first team: + +ezxml_t f1 = ezxml_parse_file("formula1.xml"); + +printf("%s\n", ezxml_get(f1, "team", 0, "driver", 1, "name", -1)->txt); +ezxml_free(f1); + +The -1 indicates the end of the argument list. That's pretty much all +there is to it. Complete API documentation can be found in ezxml.h. + +Known Limitations + +- No support for UTF-16, however UTF-8 is handled correctly. UTF-16 support is + required for XML 1.0 conformity and will be implimented for the 1.0 release. + +- Loads the entire xml document into memory at once and does not allow for + documents to be passed in a chunk at a time. Large xml files can still be + handled though through ezxml_parse_file() and ezxml_parse_fd(), which use mmap + to map the file to a virtual address space and rely on the virtual memory + system to page in data as needed. + +- Ignores DTDs. Parsing of the internal DTD subset is required for XML 1.0 + conformity and will be implimented for the 1.0 release. ezXML is not, and is + not likely to become, a validating parser. + +- In making the character content of tags easy to access, there is no way + provided to keep track of the location of sub tags relative to the character + data. Example: + + line one
+ line two
+ + The character content of the doc tag is reported as "line one\nline two", and +
is reported as a sub tag, but the location of
within the + character data is not. The function ezxml_toxml() will convert an ezXML + structure back to xml with sub tag locations intact. + +Licensing + +ezXML was written by Aaron Voisine and is distributed under +the terms of the MIT license, described in license.txt. diff --git a/packages/services/ezxml/current/doc/license.txt b/packages/services/ezxml/current/doc/license.txt new file mode 100644 --- /dev/null +++ b/packages/services/ezxml/current/doc/license.txt @@ -0,0 +1,20 @@ +Copyright 2004 Aaron Voisine + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/packages/services/ezxml/current/include/ezxml.h b/packages/services/ezxml/current/include/ezxml.h new file mode 100644 --- /dev/null +++ b/packages/services/ezxml/current/include/ezxml.h @@ -0,0 +1,169 @@ +//========================================================================== +// +// ezxml.h +// +// Simple XML parser +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2005 eCosCentric Ltd +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos 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 General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Aaron Voisine +// Contributors: Matt Jerdonek +// Date: 2005-01-31 +// Purpose: +// Description: +// +// This code is part of eCos (tm). +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +/* ezxml.h + * + * Copyright 2004 Aaron Voisine + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef _EZXML_H +#define _EZXML_H + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define EZXML_BUFSIZE 1024 + +// returns the next tag of the same name in the same section and depth or NULL +// if not found +#define ezxml_next(xml) xml->next + +// returns the tag character content or empty string if none +#define ezxml_txt(xml) xml->txt + +typedef struct ezxml *ezxml_t; +struct ezxml { + char *name; // tag name + char **attr; // tag attributes { name, value, name, value, ... NULL } + char *txt; // tag character content, empty string if none + size_t off; // tag offset in parent tag character content + ezxml_t next; // next tag with same name in this section at this depth + ezxml_t sibling; // next tag with different name in same section and depth + ezxml_t ordered; // next tag, same section and depth, in original order + ezxml_t child; // head of sub tag list, NULL if none + ezxml_t parent; // parent tag, NULL if current tag is root tag + short flags; // additional information, only used internally for now +}; + +// Given a string of xml data and its length, parses it and creates an ezxml +// structure. For efficiency, modifies the data by adding null terminators +// and decoding ampersand sequences. If you don't want this, copy the data and +// pass in the copy. Returns NULL on failure. +ezxml_t ezxml_parse_str(char *s, size_t len); + +// A wrapper for ezxml_parse_str() that accepts a file descriptor. First +// attempts to mem map the file. Failing that, reads the file into memory. +// Returns NULL on failure. +ezxml_t ezxml_parse_fd(int fd); + +// a wrapper for ezxml_parse_fd() that accepts a file name +ezxml_t ezxml_parse_file(const char *file); + +// Wrapper for ezxml_parse_str() that accepts a file stream. Reads the entire +// stream into memory and then parses it. For xml files, use ezxml_parse_file() +// or ezxml_parse_fd() +ezxml_t ezxml_parse_fp(FILE *fp); + +// returns the first child tag (one level deeper) with the given name or NULL if +// not found +ezxml_t ezxml_child(ezxml_t xml, const char *name); + +// Returns the Nth tag with the same name in the same section at the same depth +// or NULL if not found. An index of 0 returns the tag given. +ezxml_t ezxml_idx(ezxml_t xml, int idx); + +// returns the value of the requested tag attribute, or NULL if not found +const char *ezxml_attr(ezxml_t xml, const char *attr); + +// Traverses the ezxml sturcture to retrive a specific subtag. Takes a variable +// length list of tag names and indexes. Final index must be -1. Example: +// title = ezxml_get(library, "shelf", 0, "book", 2, "title", -1); +// This retrieves the title of the 3rd book on the 1st shelf of library. +// Returns NULL if not found. +ezxml_t ezxml_get(ezxml_t xml, ...); + +// Converts an ezxml structure back to xml. Returns a string of xml data that +// must be freed. +char *ezxml_toxml(ezxml_t xml); + +// returns a NULL terminated array of processing instructions for the given +// target +const char **ezxml_pi(ezxml_t xml, const char *target); + +// frees the memory allocated for an ezxml structure +void ezxml_free(ezxml_t xml); + +// returns parser error message or empty string if none +const char *ezxml_error(ezxml_t xml); + +#ifdef __cplusplus +} +#endif + +#endif // _EZXML_H diff --git a/packages/services/ezxml/current/src/ezxml.c b/packages/services/ezxml/current/src/ezxml.c new file mode 100644 --- /dev/null +++ b/packages/services/ezxml/current/src/ezxml.c @@ -0,0 +1,659 @@ +//========================================================================== +// +// crc32.c +// +// Gary S. Brown's 32 bit CRC +// +//========================================================================== +//####ECOSGPLCOPYRIGHTBEGIN#### +// ------------------------------------------- +// This file is part of eCos, the Embedded Configurable Operating System. +// Copyright (C) 2005 eCosCentric Ltd +// +// eCos is free software; you can redistribute it and/or modify it under +// the terms of the GNU General Public License as published by the Free +// Software Foundation; either version 2 or (at your option) any later version. +// +// eCos 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 General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with eCos; if not, write to the Free Software Foundation, Inc., +// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. +// +// As a special exception, if other files instantiate templates or use macros +// or inline functions from this file, or you compile this file and link it +// with other works to produce a work based on this file, this file does not +// by itself cause the resulting work to be covered by the GNU General Public +// License. However the source code for this file must still be made available +// in accordance with section (3) of the GNU General Public License. +// +// This exception does not invalidate any other reasons why a work based on +// this file might be covered by the GNU General Public License. +// +// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. +// at http://sources.redhat.com/ecos/ecos-license/ +// ------------------------------------------- +//####ECOSGPLCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): Aaron Voisine +// Contributors: Matt Jerdonek +// Date: 2005-01-31 +// Purpose: +// Description: +// +// This code is part of eCos (tm). +// +//####DESCRIPTIONEND#### +// +//========================================================================== + + +/* ezxml.c + * + * Copyright 2004 Aaron Voisine + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#ifndef __ECOS__ +#include +#endif +#include +#include "ezxml.h" + +#include + +#define EZXML_TXTM 0x80 // flag value meaning txt was malloced +#define EZXML_WS "\t\r\n " // whitespace + +// called when parser finds close of tag +#define ezxml_close_tag(root) root->cur = root->cur->parent; + +typedef struct ezxml_root *ezxml_root_t; +struct ezxml_root { // additional data for the root tag + struct ezxml xml; // is a super-struct built on top of ezxml struct + ezxml_t cur; // current xml tree insertion point + void *m; // original xml string + size_t len; // length of allocated memory for mmap, or -1 for malloc + const char *err; // error string + char ***pi; // processing instructions +}; + +// returns the first child tag with the given name or NULL if not found +ezxml_t ezxml_child(ezxml_t xml, const char *name) +{ + if (! xml) return NULL; + xml = xml->child; + while (xml && strcmp(name, xml->name)) xml = xml->sibling; + + return xml; +} + +// returns the Nth tag with the same name in the same subsection or NULL if not +// found +ezxml_t ezxml_idx(ezxml_t xml, int idx) +{ + for (; xml && idx; idx--) xml = xml->next; + return xml; +} + +// returns the value of the requested tag attribute or NULL if not found +const char *ezxml_attr(ezxml_t xml, const char *attr) +{ + int i = 0; + + if (! xml) return NULL; + while (xml->attr[i] && strcmp(attr, xml->attr[i])) i += 2; + return (xml->attr[i]) ? xml->attr[i + 1] : NULL; +} + +// same as ezxml_get but takes an alredy initialized va_list +ezxml_t ezxml_vget(ezxml_t xml, va_list ap) +{ + char *name = va_arg(ap, char *); + int idx = va_arg(ap, int); + + xml = ezxml_child(xml, name); + return (idx < 0) ? xml : ezxml_vget(ezxml_idx(xml, idx), ap); +} + +// Traverses the xml tree to retrive a specific subtag. Takes a variable +// length list of tag names and indexes. Final index must be -1. Example: +// title = ezxml_get(library, "shelf", 0, "book", 2, "title", -1); +// This retrieves the title of the 3rd book on the 1st shelf of library. +// Returns NULL if not found. +ezxml_t ezxml_get(ezxml_t xml, ...) +{ + va_list ap; + ezxml_t ret; + + va_start(ap, xml); + ret = ezxml_vget(xml, ap); + va_end(ap); + + return ret; +} + +// returns a NULL terminated array of processing instructions for the given +// target +const char **ezxml_pi(ezxml_t xml, const char *target) +{ + static const char *nopi = NULL; + ezxml_root_t root; + int i = 0; + + while (xml->parent) xml = xml->parent; + root = (ezxml_root_t)xml; + + if (! root->pi) return &nopi; + while (root->pi[i] && strcmp(target, root->pi[i][0])) i++; + return (root->pi[i]) ? (const char **)root->pi[i] + 1 : &nopi; +} + +// Converts \r or \r\n to a single \n. If decode is non-zero, decodes ampersand +// sequences in place. Returns s. +char *ezxml_decode(char *s, int decode) +{ + int b; + char *e, *ret = s; + long c, d; + + for (;;) { + while (*s && *s != '\r' && *s != '&') s++; + + if (! *s) return ret; + else if (*s == '\r') { + *(s++) = '\n'; + if (*s == '\n') memmove((void *)s, (void *)(s + 1), strlen(s)); + continue; + } + else if (! decode) { s++; continue; } + else if (! strncmp(s, "<", 4)) *(s++) = '<'; + else if (! strncmp(s, ">", 4)) *(s++) = '>'; + else if (! strncmp(s, """, 6)) *(s++) = '"'; + else if (! strncmp(s, "'", 6)) *(s++) = '\''; + else if (! strncmp(s, "&", 5)) s++; + else if (! strncmp(s, "&#", 2)) { + if (s[2] == 'x') c = strtol(s + 3, &e, 16); + else c = strtol(s + 2, &e, 10); + if (! c || *e != ';') { s++; continue; } + + if (c < 0x80) *(s++) = (char)c; // US-ASCII subset + else { // multi-byte UTF-8 sequence + for (b = 0, d = c; d; d /= 2) b++; // number of bits in c + b = (b - 2) / 5; // number of bytes in payload + *(s++) = (0xFF << (7 - b)) | (c >> (6 * b)); // head + while (b) *(s++) = 0x80 | ((c >> (6 * --b)) & 0x3F); // payload + } + } + else { s++; continue; } + + memmove((void *)s, (void *)(strchr(s, ';') + 1), strlen(strchr(s, ';'))); + } +} + +// called when parser finds start of new tag +void ezxml_open_tag(ezxml_root_t root, char *name, char **attr) +{ + ezxml_t xml = root->cur; + + if (xml->name) { // not root tag + if (xml->child) { // already have sub tags + xml = xml->child; + while (xml->ordered) xml = xml->ordered; + xml->ordered = (ezxml_t)malloc(sizeof(struct ezxml)); + xml->ordered->parent = root->cur; + root->cur = xml->ordered; + xml = xml->parent->child; + + while (strcmp(xml->name, name) && xml->sibling) xml = xml->sibling; + if (! strcmp(xml->name, name)) { // already have this tag type + while (xml->next) xml = xml->next; + xml = xml->next = root->cur; + } + else xml = xml->sibling = root->cur; + } + else { // first sub tag + xml->child = (ezxml_t)malloc(sizeof(struct ezxml)); + xml->child->parent = xml; + root->cur = xml = xml->child; + } + + xml->off = strlen(xml->parent->txt); // offset in parent char content + } + + // initialize new tag + xml->name = name; + xml->attr = attr; + xml->next = xml->child = xml->sibling = xml->ordered = NULL; + xml->txt = ""; + xml->flags = 0; +} + +// called when parser finds character content between open and closing tag +void ezxml_char_content(ezxml_root_t root, char *s, size_t len, short decode) +{ + ezxml_t xml = root->cur; + size_t l; + + if (! xml || ! xml->name || ! len) return; + + s[len] = '\0'; + ezxml_decode(s, decode); + + if (! *(xml->txt)) xml->txt = s; + else { // allocate our own memory and make a copy + l = strlen(xml->txt); + if (! (xml->flags & EZXML_TXTM)) { + xml->txt = strcpy((char *)malloc(l + len + 1), xml->txt); + xml->flags |= EZXML_TXTM; + } + else xml->txt = (char *)realloc((void *)(xml->txt), l + len + 1); + strcpy(xml->txt + l, s); + } +} + +// called when the parser finds an xml processing instruction +void ezxml_proc_inst(ezxml_root_t root, char *s, size_t len) +{ + int i = 0, j = 1; + char *target = s; + + s[len] = '\0'; // null terminate instruction + *(s += strcspn(s, EZXML_WS)) = '\0'; // null terminate target + s += strspn(s + 1, EZXML_WS) + 1; // skip whitespace after target + + if (! root->pi) *(root->pi = (char ***)malloc(sizeof(char**))) = NULL; + + while (root->pi[i] && strcmp(target, root->pi[i][0])) i++; + if (! root->pi[i]) { // new target + root->pi = (char ***)realloc(root->pi, sizeof(char **) * (i + 2)); + root->pi[i] = (char **)malloc(sizeof(char *) * 2); + root->pi[i][0] = target; + root->pi[i + 1] = NULL; // null terminate lists + root->pi[i][1] = (char *)root->pi[i + 1]; + } + + while (root->pi[i][j]) j++; + root->pi[i] = (char **)realloc(root->pi[i], sizeof(char *) * (j + 2)); + root->pi[i][j] = s; + root->pi[i][j + 1] = NULL; +} + +// set an error string and return root +ezxml_t ezxml_seterr(ezxml_root_t root, const char *err) +{ + root->err = err; + return (ezxml_t)root; +} + +// parse the given xml string and return an ezxml structure +ezxml_t ezxml_parse_str(char *s, size_t len) +{ + ezxml_root_t root = (ezxml_root_t)malloc(sizeof(struct ezxml_root)); + char *d, **attr, q, e; + static char *noattr[] = { NULL }; + int l; + + if (! root) return NULL; + + // initialize root tag + memset((void *)root, '\0', sizeof (struct ezxml_root)); + root->xml.attr = noattr; + root->cur = (ezxml_t)root; + root->m = (void *)s; + root->err = root->xml.txt = ""; + + if (! len) return ezxml_seterr(root, "root tag missing"); + e = s[len - 1]; + s[len - 1] = '\0'; + + while (*s && *s != '<') s++; // find first tag + if (! *s) return ezxml_seterr(root, "root tag missing"); + + for (;;) { + attr = noattr; + d = ++s; + + if (isalpha(*s) || *s == '_' || *s == ':') { // new tag + if (! root->cur) return ezxml_seterr(root, "unmatched closing tag"); + + s += strcspn(s, EZXML_WS "/>"); + if (isspace(*s)) *(s++) = '\0'; + + l = 0; + while (*s && *s != '/' && *s != '>') { // new tag attribute + while (isspace(*s)) s++; + + attr = (char **)((! l) ? malloc(3 * sizeof (char *)) : + realloc((void *)attr, (l + 3) * sizeof (char *))); + attr[l] = s; + + s += strcspn(s, EZXML_WS "=/>"); + if (*s == '=' || isspace(*s)) { + *(s++) = '\0'; + q = *(s += strspn(s, EZXML_WS "=")); + if (q == '"' || q == '\'') { // attribute value + attr[l + 1] = ++s; + while (*s && *s != q) s++; + if (*s) *(s++) = '\0'; + else { + free(attr); + return ezxml_seterr(root, (q == '"') ? + "missing \"" : "missing '"); + } + ezxml_decode(attr[l + 1], 1); + } + else attr[l + 1] = ""; + } + else attr[l + 1] = ""; + + attr[(l += 2)] = NULL; + } + + if (*s == '/') { // self closing tag + *(s++) = '\0'; + if ((*s && *s != '>') || (! *s && e != '>')) { + if (l) free(attr); + return ezxml_seterr(root, "missing >"); + } + ezxml_open_tag(root, d, attr); + ezxml_close_tag(root); + } + else if (*s == '>' || (! *s && e == '>')) { // open tag + q = *s; + *s = '\0'; + ezxml_open_tag(root, d, attr); + *s = q; + } + else { + if (l) free(attr); + return ezxml_seterr(root, "missing >"); + } + } + else if (*s == '/') { // close tag + if (! root->cur) return ezxml_seterr(root, "unmatched closing tag"); + ezxml_close_tag(root); + while (*s && *s != '>') s++; + if (! *s && e != '>') return ezxml_seterr(root, "missing >"); + } + else if (! strncmp(s, "!--", 3)) { // comment + do { s = strstr(s, "--"); } while (s && *(s += 2) && *s != '>'); + if (! s || (! *s && e != '>')) + return ezxml_seterr(root, "unclosed