diff --git a/CHANGES b/CHANGES index 3006f21..d603494 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,4 @@ -README - 06/03/2003 +README - 06/04/2003 ------------------- @@ -9,6 +9,17 @@ CHANGES IN Mini-XML 0.93 character entities. - mxmlSaveFile() now uses newlines as whitespace when valid to do so. + - mxmlFindElement() now also takes attribute name and + attribute value string arguments to limit the search + to specific elements with attributes and/or values. + NULL pointers can be used as "wildcards". + - Added uninstall target to makefile, and auto-reconfig + if Makefile.in or configure.in are changed. + - mxmlFindElement(), mxmlWalkNext(), and mxmlWalkPrev() + now all provide "descend" arguments to control whether + they descend into child nodes in the tree. + - Fixed some whitespace issues in mxmlLoadFile(). + - Fixed Unicode output issues in mxmlSaveFile(). CHANGES IN Mini-XML 0.92 diff --git a/Makefile.in b/Makefile.in index 4ef89f7..0df3680 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,5 +1,5 @@ # -# "$Id: Makefile.in,v 1.3 2003/06/04 00:25:59 mike Exp $" +# "$Id: Makefile.in,v 1.4 2003/06/04 16:30:39 mike Exp $" # # Makefile for mini-XML, a small XML-like file parsing library. # @@ -53,15 +53,15 @@ libdir = @libdir@ # LIBOBJS = mxml-attr.o mxml-file.o mxml-node.o mxml-search.o -OBJS = testmxml.o $(LIBOBJS) -TARGETS = libmxml.a testmxml +OBJS = mxmldoc.o testmxml.o $(LIBOBJS) +TARGETS = libmxml.a mxmldoc testmxml # # Make everything... # -all: $(TARGETS) +all: Makefile configure $(TARGETS) # @@ -86,6 +86,37 @@ install: $(TARGETS) cp mxml.h $(includedir) +# +# Uninstall everything... +# + +uninstall: + rm -f $(libdir)/libmxml.a + rm -f $(includedir)/mxml.h + + +# +# autoconf stuff... +# + +Makefile: configure Makefile.in + if test -f config.status; then \ + ./config.status --recheck; \ + ./config.status; \ + else \ + ./configure; \ + fi + +configure: configure.in + autoconf + if test -f config.status; then \ + ./config.status --recheck; \ + ./config.status; \ + else \ + ./configure; \ + fi + + # # libmxml.a # @@ -98,6 +129,16 @@ libmxml.a: $(LIBOBJS) $(LIBOBJS): mxml.h +# +# mxmldoc +# + +mxmldoc: libmxml.a mxmldoc.o + $(CC) $(LDFLAGS) -o $@ mxmldoc.o libmxml.a + +mxmldoc.o: mxml.h + + # # testmxml # @@ -116,9 +157,14 @@ testmxml: libmxml.a testmxml.o testmxml.o: mxml.h + +# +# All object files depend on the makefile... +# + $(OBJS): Makefile # -# End of "$Id: Makefile.in,v 1.3 2003/06/04 00:25:59 mike Exp $". +# End of "$Id: Makefile.in,v 1.4 2003/06/04 16:30:39 mike Exp $". # diff --git a/README b/README index eef2b03..61a51b1 100644 --- a/README +++ b/README @@ -1,11 +1,11 @@ -README - 06/03/2003 +README - 06/04/2003 ------------------- INTRODUCTION This README file describes the Mini-XML library version - 0.91. + 0.93. Mini-XML is a small XML parsing library that you can use to read XML and XML-like data files in your application without @@ -92,15 +92,39 @@ DOCUMENTATION You can find a named element/node using the "mxmlFindElement()" function: - mxml_node_t *node = mxmlFindElement(tree, tree, "name"); + mxml_node_t *node = mxmlFindElement(tree, tree, "name", "attr", + "value", MXML_DESCEND); + + The "name", "attr", and "value" arguments can be passed as + NULL to act as wildcards, e.g.: + + /* Find the first "a" element */ + node = mxmlFindElement(tree, tree, "a", NULL, NULL, MXML_DESCEND); + + /* Find the first "a" element with "href" attribute */ + node = mxmlFindElement(tree, tree, "a", "href", NULL, MXML_DESCEND); + + /* Find the first "a" element with "href" to a URL */ + node = mxmlFindElement(tree, tree, "a", "href", + "http://www.easysw.com/~mike/mxml/", + MXML_DESCEND); + + /* Find the first element with a "src" attribute*/ + node = mxmlFindElement(tree, tree, NULL, "src", NULL, MXML_DESCEND); + + /* Find the first element with a "src" = "foo.jpg" */ + node = mxmlFindElement(tree, tree, NULL, "src", "foo.jpg", + MXML_DESCEND); You can also iterate with the same function: mxml_node_t *node; - for (node = mxmlFindElement(tree, tree, "name"); + for (node = mxmlFindElement(tree, tree, "name", NULL, NULL, + MXML_DESCEND); node != NULL; - node = mxmlFindElement(node, tree, "name")) + node = mxmlFindElement(node, tree, "name", NULL, NULL, + MXML_DESCEND)) { ... do something ... } diff --git a/index.html b/index.html index 14add74..c147f4f 100644 --- a/index.html +++ b/index.html @@ -1,174 +1,199 @@ - - -
-Current Release: v0.93 [ Download Source (.tar.gz 40k) | -View Change Log ]
- -Mini-XML is a small XML parsing library that you can use to + + +
+Current Release: v0.93 [ Download Source (.tar.gz 40k) | +View Change Log ]
+ +Mini-XML is a small XML parsing library that you can use to read XML and XML-like data files in your application without requiring large non-standard libraries. Mini-XML only requires an ANSI C compatible compiler (GCC works, as do most vendors' -ANSI C compilers) and a "make" program.
+ANSI C compilers) and a "make" program. -Mini-XML was created to support the basic hierarchy provided +
Mini-XML was created to support the basic hierarchy provided by XML and some simple data types, but doesn't do validation or -other types of processing on the data.
+other types of processing on the data. -Mini-XML comes with an autoconf-based configure script; just -type the following command to get things going:
+Mini-XML comes with an autoconf-based configure script; just +type the following command to get things going:
-+-./configure -+
The default install prefix is /usr/local, which can be -overridden using the --prefix option:
+The default install prefix is /usr/local, which can be +overridden using the --prefix option:
-+-./configure --prefix=/foo -+
Once you have configured the software, type "make" to do the +
Once you have configured the software, type "make" to do the build and then run the test program to verify that things are -working, as follows:
+working, as follows: -+-make ./testmxml test.xml -+
The "install" target will install Mini-XML in the lib and -include directories:
+The "install" target will install Mini-XML in the lib and +include directories:
-+-make install -+
Once you have installed it, use the "-lmxml" option to link -your application against it.
+Once you have installed it, use the "-lmxml" option to link +your application against it.
-The documentation is currently just in this page. At some +
The documentation is currently just in this page. At some point I'll probably do some proper documentation, but for now -just read here and look at the testmxml.c source file for an +just read here and look at the testmxml.c source file for an example of reading and printing the contents of an XML file to -stdout.
+stdout. -Mini-XML provides a single header file which you include:
+Mini-XML provides a single header file which you include:
-- #include <mxml.h> -+
+ #include <mxml.h> +-
Nodes are defined by the mxml_node_t structure; the -type member defines the node type (element, integer, +
Nodes are defined by the mxml_node_t structure; the +type member defines the node type (element, integer, opaque, real, or text) which determines which value you want to -look at in the value union. New nodes can be created -using the mxmlNewElement(), mxmlNewInteger(), -mxmlNewOpaque(), mxmlNewReal(), and -mxmlNewText() functions. Only elements can have child -nodes, and the top node must be an element, usually "?xml".
+look at in the value union. New nodes can be created +using the mxmlNewElement(), mxmlNewInteger(), +mxmlNewOpaque(), mxmlNewReal(), and +mxmlNewText() functions. Only elements can have child +nodes, and the top node must be an element, usually "?xml". -You load an XML file using the mxmlLoadFile() function:
+You load an XML file using the mxmlLoadFile() function:
-+-FILE *fp; mxml_node_t *tree; fp = fopen("filename.xml", "r"); tree = mxmlLoadFile(NULL, fp, MXML_NO_CALLBACK); fclose(fp); -+
Similarly, you save an XML file using the mxmlSaveFile() -function:
+Similarly, you save an XML file using the mxmlSaveFile() +function:
-+-FILE *fp; mxml_node_t *tree; fp = fopen("filename.xml", "w"); mxmlSaveFile(tree, fp); fclose(fp); -+
You can find a named element/node using the -mxmlFindElement() function:
+You can find a named element/node using the +mxmlFindElement() function:
-- mxml_node_t *node = mxmlFindElement(tree, tree, "name"); -+
+ mxml_node_t *node = mxmlFindElement(tree, tree, "name", "attr", + "value", MXML_DESCEND); +-
You can also iterate with the same function: +
The name, attr, and value +arguments can be passed as NULL to act as wildcards, +e.g.:
-+-+ /* Find the first "a" element */ + node = mxmlFindElement(tree, tree, "a", NULL, NULL, MXML_DESCEND); + + /* Find the first "a" element with "href" attribute */ + node = mxmlFindElement(tree, tree, "a", "href", NULL, MXML_DESCEND); + + /* Find the first "a" element with "href" to a URL */ + node = mxmlFindElement(tree, tree, "a", "href", + "http://www.easysw.com/~mike/mxml/", MXML_DESCEND); + + /* Find the first element with a "src" attribute*/ + node = mxmlFindElement(tree, tree, NULL, "src", NULL, MXML_DESCEND); + + /* Find the first element with a "src" = "foo.jpg" */ + node = mxmlFindElement(tree, tree, NULL, "src", "foo.jpg", MXML_DESCEND); ++ +You can also iterate with the same function:
+ +mxml_node_t *node; - for (node = mxmlFindElement(tree, tree, "name"); + for (node = mxmlFindElement(tree, tree, "name", NULL, NULL, MXML_DESCEND); node != NULL; - node = mxmlFindElement(node, tree, "name")) + node = mxmlFindElement(node, tree, "name", NULL, NULL, MXML_DESCEND)) { ... do something ... } -+
Finally, once you are done with the XML data, use the -mxmlDelete() function to recursively free the memory -that is used for a particular node or the entire tree:
+Finally, once you are done with the XML data, use the +mxmlDelete() function to recursively free the memory +that is used for a particular node or the entire tree:
-+-mxmlDelete(tree); -+
You can email me at "mxml at easysw dot com" to +
You can email me at "mxml at easysw dot com" to report problems and/or ask for help. Just don't expect an -instant response, as I get a lot of email...
+instant response, as I get a lot of email... -The Mini-XML library is Copyright 2003 by Michael Sweet.
+The Mini-XML library is Copyright 2003 by Michael Sweet.
-This library is free software; you can redistribute it +
This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any -later version.
+later version. -This library is distributed in the hope that it will be +
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 Library General Public License for -more details.
+more details. -You should have received a copy of the GNU Library General +
You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA -02139, USA.
+02139, USA. - - + + diff --git a/mxml-file.c b/mxml-file.c index aed38e3..19ab436 100644 --- a/mxml-file.c +++ b/mxml-file.c @@ -1,5 +1,5 @@ /* - * "$Id: mxml-file.c,v 1.5 2003/06/04 02:34:29 mike Exp $" + * "$Id: mxml-file.c,v 1.6 2003/06/04 16:30:40 mike Exp $" * * File loading code for mini-XML, a small XML-like file parsing library. * @@ -131,6 +131,32 @@ mxmlLoadFile(mxml_node_t *top, /* I - Top node */ break; } } + else if (isspace(ch) && type == MXML_TEXT) + whitespace = 1; + + /* + * Add lone whitespace node if we are starting a new element and have + * existing whitespace... + */ + + if (ch == '<' && whitespace && type == MXML_TEXT) + { + /* + * Peek at the next character and only do this if we are starting + * an open tag... + */ + + ch = getc(fp); + ungetc(ch, fp); + + if (ch != '/') + { + mxmlNewText(parent, whitespace, ""); + whitespace = 0; + } + + ch = '<'; + } if (ch == '<') { @@ -144,40 +170,114 @@ mxmlLoadFile(mxml_node_t *top, /* I - Top node */ if (isspace(ch) || ch == '>' || (ch == '/' && bufptr > buffer)) break; else if (bufptr < (buffer + sizeof(buffer) - 1)) + { *bufptr++ = ch; + if ((bufptr - buffer) == 3 && !strncmp(buffer, "!--", 3)) + break; + } + *bufptr = '\0'; - bufptr = buffer; if (!strcmp(buffer, "!--")) { /* - * Skip comment... + * Gather rest of comment... */ - buffer[3] = '\0'; - while ((ch = getc(fp)) != EOF) { - *bufptr++ = ch; - - if ((bufptr - buffer) == 3) + if (ch == '>' && bufptr > (buffer + 4) && + !strncmp(bufptr - 2, "--", 2)) + break; + else if (bufptr < (buffer + sizeof(buffer) - 1)) + *bufptr++ = ch; + else { - if (!strcmp(buffer, "-->")) - break; + fprintf(stderr, "Comment too long in file under parent <%s>!\n", + parent ? parent->value.element.name : "null"); + break; + } + } - buffer[0] = buffer[1]; - buffer[1] = buffer[2]; - bufptr --; + /* + * Error out if we didn't get the whole comment... + */ + + if (ch != '>') + break; + + /* + * Otherwise add this as an element under the current parent... + */ + + *bufptr = '\0'; + + if (!mxmlNewElement(parent, buffer)) + { + /* + * Just print error for now... + */ + + fprintf(stderr, "Unable to add comment node to parent <%s>!\n", + parent ? parent->value.element.name : "null"); + break; + } + } + else if (buffer[0] == '!') + { + /* + * Gather rest of declaration... + */ + + do + { + if (ch == '>') + break; + else if (bufptr < (buffer + sizeof(buffer) - 1)) + *bufptr++ = ch; + else + { + fprintf(stderr, "Declaration too long in file under parent <%s>!\n", + parent ? parent->value.element.name : "null"); + break; } } + while ((ch = getc(fp)) != EOF); - bufptr = buffer; + /* + * Error out if we didn't get the whole declaration... + */ - if (ch == EOF) + if (ch != '>') break; - else - continue; + + /* + * Otherwise add this as an element under the current parent... + */ + + *bufptr = '\0'; + + node = mxmlNewElement(parent, buffer); + if (!node) + { + /* + * Just print error for now... + */ + + fprintf(stderr, "Unable to add declaration node to parent <%s>!\n", + parent ? parent->value.element.name : "null"); + break; + } + + /* + * Descend into this node, setting the value type as needed... + */ + + parent = node; + + if (cb) + type = (*cb)(parent); } else if (buffer[0] == '/') { @@ -260,6 +360,8 @@ mxmlLoadFile(mxml_node_t *top, /* I - Top node */ type = (*cb)(parent); } } + + bufptr = buffer; } else if (ch == '&') { @@ -635,7 +737,7 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */ if (node->child) { /* - * The ?xml element is a special-case and has no end tag... + * The ? and ! elements are special-cases and have no end tags... */ if (node->value.element.name[0] == '?') @@ -653,7 +755,8 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */ if ((col = mxml_write_node(node->child, fp, col)) < 0) return (-1); - if (node->value.element.name[0] != '?') + if (node->value.element.name[0] != '?' && + node->value.element.name[0] != '!') { if ((n = fprintf(fp, "%s>", node->value.element.name)) < 0) return (-1); @@ -661,6 +764,13 @@ mxml_write_node(mxml_node_t *node, /* I - Node to write */ col += n; } } + else if (node->value.element.name[0] == '!') + { + if (putc('>', fp) < 0) + return (-1); + else + col ++; + } else if (fputs("/>", fp) < 0) return (-1); else @@ -772,6 +882,45 @@ mxml_write_string(const char *s, /* I - String to write */ if (fputs("<", fp) < 0) return (-1); } + else if (*s == '>') + { + if (fputs(">", fp) < 0) + return (-1); + } + else if (*s & 128) + { + /* + * Convert UTF-8 to Unicode constant... + */ + + int ch; /* Unicode character */ + + + ch = *s & 255; + + if ((ch & 0xe0) == 0xc0) + { + ch = ((ch & 0x1f) << 6) | (s[1] & 0x3f); + s ++; + } + else if ((ch & 0xf0) == 0xe0) + { + ch = ((((ch * 0x0f) << 6) | (s[1] & 0x3f)) << 6) | (s[2] & 0x3f); + s += 2; + } + + if (ch == 0xa0) + { + /* + * Handle non-breaking space as-is... + */ + + if (fputs(" ", fp) < 0) + return (-1); + } + else if (fprintf(fp, "%x;", ch) < 0) + return (-1); + } else if (putc(*s, fp) < 0) return (-1); @@ -784,5 +933,5 @@ mxml_write_string(const char *s, /* I - String to write */ /* - * End of "$Id: mxml-file.c,v 1.5 2003/06/04 02:34:29 mike Exp $". + * End of "$Id: mxml-file.c,v 1.6 2003/06/04 16:30:40 mike Exp $". */ diff --git a/mxml-search.c b/mxml-search.c index 0a545df..857472a 100644 --- a/mxml-search.c +++ b/mxml-search.c @@ -1,5 +1,5 @@ /* - * "$Id: mxml-search.c,v 1.2 2003/06/03 20:40:01 mike Exp $" + * "$Id: mxml-search.c,v 1.3 2003/06/04 16:30:40 mike Exp $" * * Search/navigation functions for mini-XML, a small XML-like file * parsing library. @@ -21,7 +21,6 @@ * mxmlFindElement() - Find the named element. * mxmlWalkNext() - Walk to the next logical node in the tree. * mxmlWalkPrev() - Walk to the previous logical node in the tree. - * mxml_walk_next() - Walk to the next logical node in the tree. */ /* @@ -31,14 +30,6 @@ #include "mxml.h" -/* - * Local functions... - */ - -mxml_node_t *mxml_walk_next(mxml_node_t *node, mxml_node_t *top, - int descend); - - /* * 'mxmlFindElement()' - Find the named element. */ @@ -46,13 +37,26 @@ mxml_node_t *mxml_walk_next(mxml_node_t *node, mxml_node_t *top, mxml_node_t * /* O - Element node or NULL */ mxmlFindElement(mxml_node_t *node, /* I - Current node */ mxml_node_t *top, /* I - Top node */ - const char *name) /* I - Element name */ + const char *name, /* I - Element name or NULL for any */ + const char *attr, /* I - Attribute name, or NULL for none */ + const char *value, /* I - Attribute value, or NULL for any */ + int descend) /* I - Descend into tree? */ { + const char *temp; /* Current attribute value */ + + + /* + * Range check input... + */ + + if (!node || !top || (!attr && value)) + return (NULL); + /* * Start with the next node... */ - node = mxmlWalkNext(node, top); + node = mxmlWalkNext(node, top, descend); /* * Loop until we find a matching element... @@ -66,14 +70,38 @@ mxmlFindElement(mxml_node_t *node, /* I - Current node */ if (node->type == MXML_ELEMENT && node->value.element.name && - !strcmp(node->value.element.name, name)) - return (node); + (!name || !strcmp(node->value.element.name, name))) + { + /* + * See if we need to check for an attribute... + */ + + if (!attr) + return (node); /* No attribute search, return it... */ + + /* + * Check for the attribute... + */ + + if ((temp = mxmlElementGetAttr(node, attr)) != NULL) + { + /* + * OK, we have the attribute, does it match? + */ + + if (!value || !strcmp(value, temp)) + return (node); /* Yes, return it... */ + } + } /* - * Nope, move on to the next... + * No match, move on to the next node... */ - node = mxmlWalkNext(node, top); + if (descend == MXML_DESCEND) + node = mxmlWalkNext(node, top, MXML_DESCEND); + else + node = node->next; } return (NULL); @@ -86,9 +114,29 @@ mxmlFindElement(mxml_node_t *node, /* I - Current node */ mxml_node_t * /* O - Next node or NULL */ mxmlWalkNext(mxml_node_t *node, /* I - Current node */ - mxml_node_t *top) /* I - Top node */ + mxml_node_t *top, /* I - Top node */ + int descend) /* I - Descend into tree? */ { - return (mxml_walk_next(node, top, 1)); + if (!node) + return (NULL); + else if (node->child && descend) + return (node->child); + else if (node->next) + return (node->next); + else if (node->parent != top) + { + node = node->parent; + + while (!node->next) + if (node->parent == top) + return (NULL); + else + node = node->parent; + + return (node->next); + } + else + return (NULL); } @@ -98,41 +146,36 @@ mxmlWalkNext(mxml_node_t *node, /* I - Current node */ mxml_node_t * /* O - Previous node or NULL */ mxmlWalkPrev(mxml_node_t *node, /* I - Current node */ - mxml_node_t *top) /* I - Top node */ + mxml_node_t *top, /* I - Top node */ + int descend) /* I - Descend into tree? */ { if (!node) return (NULL); else if (node->prev) - return (node->prev); - else if (node->parent != top) - return (node->parent); - else - return (NULL); -} + { + if (node->prev->last_child && descend) + { + /* + * Find the last child under the previous node... + */ + node = node->prev->last_child; -/* - * 'mxml_walk_next()' - Walk to the next logical node in the tree. - */ + while (node->last_child) + node = node->last_child; -mxml_node_t * /* O - Next node or NULL */ -mxml_walk_next(mxml_node_t *node, /* I - Current node */ - mxml_node_t *top, /* I - Top node */ - int descend) /* I - 1 = descend, 0 = don't */ -{ - if (!node) - return (NULL); - else if (node->child && descend) - return (node->child); - else if (node->next) - return (node->next); + return (node); + } + else + return (node->prev); + } else if (node->parent != top) - return (mxml_walk_next(node->parent, top, 0)); + return (node->parent); else return (NULL); } /* - * End of "$Id: mxml-search.c,v 1.2 2003/06/03 20:40:01 mike Exp $". + * End of "$Id: mxml-search.c,v 1.3 2003/06/04 16:30:40 mike Exp $". */ diff --git a/mxml.h b/mxml.h index 0d2d1d4..d18c38a 100644 --- a/mxml.h +++ b/mxml.h @@ -1,5 +1,5 @@ /* - * "$Id: mxml.h,v 1.4 2003/06/04 02:34:30 mike Exp $" + * "$Id: mxml.h,v 1.5 2003/06/04 16:30:40 mike Exp $" * * Header file for mini-XML, a small XML-like file parsing library. * @@ -31,6 +31,7 @@ # include